Skip to content

When connection queue is full, respond with a 503 error. #745

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

itamarst
Copy link

@itamarst itamarst commented May 22, 2025

❓ What kind of change does this PR introduce?

  • 🐞 bug fix
  • 🐣 feature
  • πŸ“‹ docs update
  • πŸ“‹ tests/coverage improvement
  • πŸ“‹ refactoring
  • πŸ’₯ other

❓ What is the current behavior? (You can also link to an open issue here)

When connections can't be handled, the connection is just closed.

❓ What is the new behavior (if this is a feature change)?

A 503 error is returned, as suggested in TODO comment in relevant part of the code.

Since writing to the socket is blocking, this is done in a thread.

πŸ“‹ Other information:

For some reason OpenStack Ironic needs this apparently, I didn't dig into why.

πŸ“‹ Contribution checklist:

(If you're a first-timer, check out
this guide on making great pull requests)

  • I wrote descriptive pull request text above
  • I think the code is well written
  • I wrote good commit messages
  • I have squashed related commits together after
    the changes have been approved
  • Unit tests for the changes exist
  • Integration tests for the changes exist (if applicable)
  • I used the same coding conventions as the rest of the project
  • The new code doesn't generate linter offenses
  • Documentation reflects the changes
  • The PR relates to only one subject with a clear title
    and description in grammatically correct, complete sentences

This change is Reviewable

Copy link

codecov bot commented May 22, 2025

❌ 21 Tests Failed:

Tests completed Failed Passed Skipped
8039 21 8018 376
View the top 3 failed test(s) by shortest run time
cheroot.test.test_conn::test_streaming_10[False]
Stack Traces | 0s run time
self = <cheroot.makefile.StreamWriter name=-1>

    #x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92m__del__#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m):#x1B[90m#x1B[39;49;00m
    #x1B[90m    #x1B[39;49;00m#x1B[33m"""Destructor.  Calls close()."""#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
            closed = #x1B[96mself#x1B[39;49;00m.closed#x1B[90m#x1B[39;49;00m
        #x1B[94mexcept#x1B[39;49;00m #x1B[96mAttributeError#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
            #x1B[90m# If getting closed fails, then the object is probably#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            #x1B[90m# in an unusable state, so ignore.#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            #x1B[94mreturn#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[94mif#x1B[39;49;00m closed:#x1B[90m#x1B[39;49;00m
            #x1B[94mreturn#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[94mif#x1B[39;49;00m dealloc_warn := #x1B[96mgetattr#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33m_dealloc_warn#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mNone#x1B[39;49;00m):#x1B[90m#x1B[39;49;00m
            dealloc_warn(#x1B[96mself#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[90m# If close() fails, the caller logs the exception with#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[90m# sys.unraisablehook. close() must be called at the end at __del__().#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
>       #x1B[96mself#x1B[39;49;00m.close()#x1B[90m#x1B[39;49;00m

closed     = False
dealloc_warn = <bound method _BufferedIOMixin._dealloc_warn of <cheroot.makefile.StreamWriter name=-1>>
self       = <cheroot.makefile.StreamWriter name=-1>

#x1B[1m#x1B[31mC:\hostedtoolcache\windows\Python\3.14.0-beta.2\x64\Lib\_pyio.py#x1B[0m:415: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
#x1B[1m#x1B[31mC:\hostedtoolcache\windows\Python\3.14.0-beta.2\x64\Lib\_pyio.py#x1B[0m:1310: in close
    #x1B[0m#x1B[96mself#x1B[39;49;00m.flush()#x1B[90m#x1B[39;49;00m
        self       = <cheroot.makefile.StreamWriter name=-1>
#x1B[1m#x1B[31mC:\hostedtoolcache\windows\Python\3.14.0-beta.2\x64\Lib\_pyio.py#x1B[0m:1271: in flush
    #x1B[0m#x1B[96mself#x1B[39;49;00m._flush_unlocked()#x1B[90m#x1B[39;49;00m
        self       = <cheroot.makefile.StreamWriter name=-1>
#x1B[1m#x1B[31mcheroot\makefile.py#x1B[0m:32: in _flush_unlocked
    #x1B[0mn = #x1B[96mself#x1B[39;49;00m.raw.write(#x1B[96mbytes#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m._write_buf))#x1B[90m#x1B[39;49;00m
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
        self       = <cheroot.makefile.StreamWriter name=-1>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <socket.SocketIO object at 0x000001891996B940>, b = b'1'

    #x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mwrite#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m, b):#x1B[90m#x1B[39;49;00m
    #x1B[90m    #x1B[39;49;00m#x1B[33m"""Write the given bytes or bytearray object *b* to the socket#x1B[39;49;00m
    #x1B[33m    and return the number of bytes written.  This can be less than#x1B[39;49;00m
    #x1B[33m    len(b) if not all data could be written.  If the socket is#x1B[39;49;00m
    #x1B[33m    non-blocking and no bytes could be written None is returned.#x1B[39;49;00m
    #x1B[33m    """#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[96mself#x1B[39;49;00m._checkClosed()#x1B[90m#x1B[39;49;00m
        #x1B[96mself#x1B[39;49;00m._checkWritable()#x1B[90m#x1B[39;49;00m
        #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
>           #x1B[94mreturn#x1B[39;49;00m #x1B[96mself#x1B[39;49;00m._sock.send(b)#x1B[90m#x1B[39;49;00m
                   ^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE           OSError: [WinError 10038] An operation was attempted on something that is not a socket#x1B[0m

b          = b'1'
self       = <socket.SocketIO object at 0x000001891996B940>

#x1B[1m#x1B[31mC:\hostedtoolcache\windows\Python\3.14.0-beta.2\x64\Lib\socket.py#x1B[0m:743: OSError

#x1B[33mThe above exception was the direct cause of the following exception:#x1B[0m

cls = <class '_pytest.runner.CallInfo'>
func = <function call_and_report.<locals>.<lambda> at 0x0000018919D533D0>
when = 'setup'
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)

    #x1B[0m#x1B[37m@classmethod#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mfrom_call#x1B[39;49;00m(#x1B[90m#x1B[39;49;00m
        #x1B[96mcls#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
        func: Callable[[], TResult],#x1B[90m#x1B[39;49;00m
        when: Literal[#x1B[33m"#x1B[39;49;00m#x1B[33mcollect#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33msetup#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mcall#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mteardown#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m],#x1B[90m#x1B[39;49;00m
        reraise: #x1B[96mtype#x1B[39;49;00m[#x1B[96mBaseException#x1B[39;49;00m] | #x1B[96mtuple#x1B[39;49;00m[#x1B[96mtype#x1B[39;49;00m[#x1B[96mBaseException#x1B[39;49;00m], ...] | #x1B[94mNone#x1B[39;49;00m = #x1B[94mNone#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
    ) -> CallInfo[TResult]:#x1B[90m#x1B[39;49;00m
    #x1B[90m    #x1B[39;49;00m#x1B[33m"""Call func, wrapping the result in a CallInfo.#x1B[39;49;00m
    #x1B[33m#x1B[39;49;00m
    #x1B[33m    :param func:#x1B[39;49;00m
    #x1B[33m        The function to call. Called without arguments.#x1B[39;49;00m
    #x1B[33m    :type func: Callable[[], _pytest.runner.TResult]#x1B[39;49;00m
    #x1B[33m    :param when:#x1B[39;49;00m
    #x1B[33m        The phase in which the function is called.#x1B[39;49;00m
    #x1B[33m    :param reraise:#x1B[39;49;00m
    #x1B[33m        Exception or exceptions that shall propagate if raised by the#x1B[39;49;00m
    #x1B[33m        function, instead of being wrapped in the CallInfo.#x1B[39;49;00m
    #x1B[33m    """#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        excinfo = #x1B[94mNone#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        instant = timing.Instant()#x1B[90m#x1B[39;49;00m
        #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
>           result: TResult | #x1B[94mNone#x1B[39;49;00m = func()#x1B[90m#x1B[39;49;00m
                                     ^^^^^^#x1B[90m#x1B[39;49;00m

cls        = <class '_pytest.runner.CallInfo'>
duration   = Duration(start=Instant(time=1750178961.6742344, perf_count=302.6554038), stop=Instant(time=1750178961.778857, perf_count=302.7600252))
excinfo    = <ExceptionInfo PytestUnraisableExceptionWarning('Exception ignored while calling deallocator <function IOBase.__del__ at 0x000001891772F480>: None\n') tblen=13>
func       = <function call_and_report.<locals>.<lambda> at 0x0000018919D533D0>
instant    = Instant(time=1750178961.6742344, perf_count=302.6554038)
reraise    = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)
result     = None
when       = 'setup'

#x1B[1m#x1B[31m.tox\py\Lib\site-packages\_pytest\runner.py#x1B[0m:344: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
#x1B[1m#x1B[31m.tox\py\Lib\site-packages\_pytest\runner.py#x1B[0m:246: in <lambda>
    #x1B[0m#x1B[94mlambda#x1B[39;49;00m: runtest_hook(item=item, **kwds), when=when, reraise=reraise#x1B[90m#x1B[39;49;00m
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
        item       = <Function test_streaming_10[False]>
        kwds       = {}
        runtest_hook = <HookCaller 'pytest_runtest_setup'>
#x1B[1m#x1B[31m.tox\py\Lib\site-packages\pluggy\_hooks.py#x1B[0m:512: in __call__
    #x1B[0m#x1B[94mreturn#x1B[39;49;00m #x1B[96mself#x1B[39;49;00m._hookexec(#x1B[96mself#x1B[39;49;00m.name, #x1B[96mself#x1B[39;49;00m._hookimpls.copy(), kwargs, firstresult)#x1B[90m#x1B[39;49;00m
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
        firstresult = False
        kwargs     = {'item': <Function test_streaming_10[False]>}
        self       = <HookCaller 'pytest_runtest_setup'>
#x1B[1m#x1B[31m.tox\py\Lib\site-packages\pluggy\_manager.py#x1B[0m:120: in _hookexec
    #x1B[0m#x1B[94mreturn#x1B[39;49;00m #x1B[96mself#x1B[39;49;00m._inner_hookexec(hook_name, methods, kwargs, firstresult)#x1B[90m#x1B[39;49;00m
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
        firstresult = False
        hook_name  = 'pytest_runtest_setup'
        kwargs     = {'item': <Function test_streaming_10[False]>}
        methods    = [<HookImpl plugin_name='threadexception', plugin=<module '_pytest.threadexception' from 'C:\\a\\cheroot\\cheroot\\.tox...tIOWrapper name='nul' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>>, ...]
        self       = <_pytest.config.PytestPluginManager object at 0x0000018916343230>
#x1B[1m#x1B[31m.tox\py\Lib\site-packages\_pytest\logging.py#x1B[0m:843: in pytest_runtest_setup
    #x1B[0m#x1B[94myield#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        empty      = {'setup': []}
        item       = <Function test_streaming_10[False]>
        self       = <_pytest.logging.LoggingPlugin object at 0x0000018917DEA120>
#x1B[1m#x1B[31m.tox\py\Lib\site-packages\_pytest\capture.py#x1B[0m:895: in pytest_runtest_setup
    #x1B[0m#x1B[94mreturn#x1B[39;49;00m (#x1B[94myield#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
            ^^^^^#x1B[90m#x1B[39;49;00m
        item       = <Function test_streaming_10[False]>
        self       = <CaptureManager _method='fd' _global_capturing=<MultiCapture out=<FDCapture 1 oldfd=8 _state='suspended' tmpfile=<Enco..._io.TextIOWrapper name='nul' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>
#x1B[1m#x1B[31m.tox\py\Lib\site-packages\_pytest\unraisableexception.py#x1B[0m:148: in pytest_runtest_setup
    #x1B[0mcollect_unraisable(item.config)#x1B[90m#x1B[39;49;00m
        item       = <Function test_streaming_10[False]>
#x1B[1m#x1B[31m.tox\py\Lib\site-packages\_pytest\unraisableexception.py#x1B[0m:77: in collect_unraisable
    #x1B[0m#x1B[94mraise#x1B[39;49;00m errors[#x1B[94m0#x1B[39;49;00m]#x1B[90m#x1B[39;49;00m
        config     = <_pytest.config.Config object at 0x0000018916CBC830>
        msg        = 'Exception ignored while calling deallocator <function IOBase.__del__ at 0x000001891772F480>: None\n\nTraceback (most ...        ~~~~~~~~~~~~~~~^^^\nOSError: [WinError 10038] An operation was attempted on something that is not a socket\n\n'
        pop_unraisable = <built-in method pop of collections.deque object at 0x0000018917A24D60>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

config = <_pytest.config.Config object at 0x0000018916CBC830>

    #x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mcollect_unraisable#x1B[39;49;00m(config: Config) -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
        pop_unraisable = config.stash[unraisable_exceptions].pop#x1B[90m#x1B[39;49;00m
        errors: #x1B[96mlist#x1B[39;49;00m[pytest.PytestUnraisableExceptionWarning | #x1B[96mRuntimeError#x1B[39;49;00m] = []#x1B[90m#x1B[39;49;00m
        meta = #x1B[94mNone#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        hook_error = #x1B[94mNone#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
            #x1B[94mwhile#x1B[39;49;00m #x1B[94mTrue#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
                #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
                    meta = pop_unraisable()#x1B[90m#x1B[39;49;00m
                #x1B[94mexcept#x1B[39;49;00m #x1B[96mIndexError#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
                    #x1B[94mbreak#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
                #x1B[94mif#x1B[39;49;00m #x1B[96misinstance#x1B[39;49;00m(meta, #x1B[96mBaseException#x1B[39;49;00m):#x1B[90m#x1B[39;49;00m
                    hook_error = #x1B[96mRuntimeError#x1B[39;49;00m(#x1B[33m"#x1B[39;49;00m#x1B[33mFailed to process unraisable exception#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
                    hook_error.__cause__ = meta#x1B[90m#x1B[39;49;00m
                    errors.append(hook_error)#x1B[90m#x1B[39;49;00m
                    #x1B[94mcontinue#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
                msg = meta.msg#x1B[90m#x1B[39;49;00m
                #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
>                   warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE                   pytest.PytestUnraisableExceptionWarning: Exception ignored while calling deallocator <function IOBase.__del__ at 0x000001891772F480>: None#x1B[0m

config     = <_pytest.config.Config object at 0x0000018916CBC830>
msg        = 'Exception ignored while calling deallocator <function IOBase.__del__ at 0x000001891772F480>: None\n\nTraceback (most ...        ~~~~~~~~~~~~~~~^^^\nOSError: [WinError 10038] An operation was attempted on something that is not a socket\n\n'
pop_unraisable = <built-in method pop of collections.deque object at 0x0000018917A24D60>

#x1B[1m#x1B[31m.tox\py\Lib\site-packages\_pytest\unraisableexception.py#x1B[0m:65: PytestUnraisableExceptionWarning
cheroot.test.test_conn::test_streaming_11[True]
Stack Traces | 0s run time
self = <cheroot.makefile.StreamWriter name=-1>

    #x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92m__del__#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m):#x1B[90m#x1B[39;49;00m
    #x1B[90m    #x1B[39;49;00m#x1B[33m"""Destructor.  Calls close()."""#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
            closed = #x1B[96mself#x1B[39;49;00m.closed#x1B[90m#x1B[39;49;00m
        #x1B[94mexcept#x1B[39;49;00m #x1B[96mAttributeError#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
            #x1B[90m# If getting closed fails, then the object is probably#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            #x1B[90m# in an unusable state, so ignore.#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            #x1B[94mreturn#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[94mif#x1B[39;49;00m closed:#x1B[90m#x1B[39;49;00m
            #x1B[94mreturn#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[94mif#x1B[39;49;00m dealloc_warn := #x1B[96mgetattr#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33m_dealloc_warn#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mNone#x1B[39;49;00m):#x1B[90m#x1B[39;49;00m
            dealloc_warn(#x1B[96mself#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[90m# If close() fails, the caller logs the exception with#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[90m# sys.unraisablehook. close() must be called at the end at __del__().#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
>       #x1B[96mself#x1B[39;49;00m.close()#x1B[90m#x1B[39;49;00m

closed     = False
dealloc_warn = <bound method _BufferedIOMixin._dealloc_warn of <cheroot.makefile.StreamWriter name=-1>>
self       = <cheroot.makefile.StreamWriter name=-1>

#x1B[1m#x1B[.../hostedtoolcache/Python/3.14.0-beta..../arm64/lib/python3.14/_pyio.py#x1B[0m:415: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
#x1B[1m#x1B[.../hostedtoolcache/Python/3.14.0-beta..../arm64/lib/python3.14/_pyio.py#x1B[0m:1310: in close
    #x1B[0m#x1B[96mself#x1B[39;49;00m.flush()#x1B[90m#x1B[39;49;00m
        self       = <cheroot.makefile.StreamWriter name=-1>
#x1B[1m#x1B[.../hostedtoolcache/Python/3.14.0-beta..../arm64/lib/python3.14/_pyio.py#x1B[0m:1271: in flush
    #x1B[0m#x1B[96mself#x1B[39;49;00m._flush_unlocked()#x1B[90m#x1B[39;49;00m
        self       = <cheroot.makefile.StreamWriter name=-1>
#x1B[1m#x1B[31mcheroot/makefile.py#x1B[0m:32: in _flush_unlocked
    #x1B[0mn = #x1B[96mself#x1B[39;49;00m.raw.write(#x1B[96mbytes#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m._write_buf))#x1B[90m#x1B[39;49;00m
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
        self       = <cheroot.makefile.StreamWriter name=-1>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <socket.SocketIO object at 0xff3dd8932110>, b = b'1'

    #x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mwrite#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m, b):#x1B[90m#x1B[39;49;00m
    #x1B[90m    #x1B[39;49;00m#x1B[33m"""Write the given bytes or bytearray object *b* to the socket#x1B[39;49;00m
    #x1B[33m    and return the number of bytes written.  This can be less than#x1B[39;49;00m
    #x1B[33m    len(b) if not all data could be written.  If the socket is#x1B[39;49;00m
    #x1B[33m    non-blocking and no bytes could be written None is returned.#x1B[39;49;00m
    #x1B[33m    """#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[96mself#x1B[39;49;00m._checkClosed()#x1B[90m#x1B[39;49;00m
        #x1B[96mself#x1B[39;49;00m._checkWritable()#x1B[90m#x1B[39;49;00m
        #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
>           #x1B[94mreturn#x1B[39;49;00m #x1B[96mself#x1B[39;49;00m._sock.send(b)#x1B[90m#x1B[39;49;00m
                   ^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE           OSError: [Errno 9] Bad file descriptor#x1B[0m

b          = b'1'
self       = <socket.SocketIO object at 0xff3dd8932110>

#x1B[1m#x1B[.../hostedtoolcache/Python/3.14.0-beta..../arm64/lib/python3.14/socket.py#x1B[0m:743: OSError

#x1B[33mThe above exception was the direct cause of the following exception:#x1B[0m

cls = <class '_pytest.runner.CallInfo'>
func = <function call_and_report.<locals>.<lambda> at 0xff3dd22b0f60>
when = 'setup'
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)

    #x1B[0m#x1B[37m@classmethod#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mfrom_call#x1B[39;49;00m(#x1B[90m#x1B[39;49;00m
        #x1B[96mcls#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
        func: Callable[[], TResult],#x1B[90m#x1B[39;49;00m
        when: Literal[#x1B[33m"#x1B[39;49;00m#x1B[33mcollect#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33msetup#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mcall#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mteardown#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m],#x1B[90m#x1B[39;49;00m
        reraise: #x1B[96mtype#x1B[39;49;00m[#x1B[96mBaseException#x1B[39;49;00m] | #x1B[96mtuple#x1B[39;49;00m[#x1B[96mtype#x1B[39;49;00m[#x1B[96mBaseException#x1B[39;49;00m], ...] | #x1B[94mNone#x1B[39;49;00m = #x1B[94mNone#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
    ) -> CallInfo[TResult]:#x1B[90m#x1B[39;49;00m
    #x1B[90m    #x1B[39;49;00m#x1B[33m"""Call func, wrapping the result in a CallInfo.#x1B[39;49;00m
    #x1B[33m#x1B[39;49;00m
    #x1B[33m    :param func:#x1B[39;49;00m
    #x1B[33m        The function to call. Called without arguments.#x1B[39;49;00m
    #x1B[33m    :type func: Callable[[], _pytest.runner.TResult]#x1B[39;49;00m
    #x1B[33m    :param when:#x1B[39;49;00m
    #x1B[33m        The phase in which the function is called.#x1B[39;49;00m
    #x1B[33m    :param reraise:#x1B[39;49;00m
    #x1B[33m        Exception or exceptions that shall propagate if raised by the#x1B[39;49;00m
    #x1B[33m        function, instead of being wrapped in the CallInfo.#x1B[39;49;00m
    #x1B[33m    """#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        excinfo = #x1B[94mNone#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        instant = timing.Instant()#x1B[90m#x1B[39;49;00m
        #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
>           result: TResult | #x1B[94mNone#x1B[39;49;00m = func()#x1B[90m#x1B[39;49;00m
                                     ^^^^^^#x1B[90m#x1B[39;49;00m

cls        = <class '_pytest.runner.CallInfo'>
duration   = Duration(start=Instant(time=1750178776.5375755, perf_count=176.472515623), stop=Instant(time=1750178776.638654, perf_count=176.573594755))
excinfo    = <ExceptionInfo PytestUnraisableExceptionWarning('Exception ignored while calling deallocator <function IOBase.__del__ at 0xff3dd38da400>: None\n') tblen=13>
func       = <function call_and_report.<locals>.<lambda> at 0xff3dd22b0f60>
instant    = Instant(time=1750178776.5375755, perf_count=176.472515623)
reraise    = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)
result     = None
when       = 'setup'

#x1B[1m#x1B[31m..../py/lib/python3.14....../site-packages/_pytest/runner.py#x1B[0m:344: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
#x1B[1m#x1B[31m..../py/lib/python3.14....../site-packages/_pytest/runner.py#x1B[0m:246: in <lambda>
    #x1B[0m#x1B[94mlambda#x1B[39;49;00m: runtest_hook(item=item, **kwds), when=when, reraise=reraise#x1B[90m#x1B[39;49;00m
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
        item       = <Function test_streaming_11[True]>
        kwds       = {}
        runtest_hook = <HookCaller 'pytest_runtest_setup'>
#x1B[1m#x1B[31m..../py/lib/python3.14.../site-packages/pluggy/_hooks.py#x1B[0m:512: in __call__
    #x1B[0m#x1B[94mreturn#x1B[39;49;00m #x1B[96mself#x1B[39;49;00m._hookexec(#x1B[96mself#x1B[39;49;00m.name, #x1B[96mself#x1B[39;49;00m._hookimpls.copy(), kwargs, firstresult)#x1B[90m#x1B[39;49;00m
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
        firstresult = False
        kwargs     = {'item': <Function test_streaming_11[True]>}
        self       = <HookCaller 'pytest_runtest_setup'>
#x1B[1m#x1B[31m..../py/lib/python3.14.../site-packages/pluggy/_manager.py#x1B[0m:120: in _hookexec
    #x1B[0m#x1B[94mreturn#x1B[39;49;00m #x1B[96mself#x1B[39;49;00m._inner_hookexec(hook_name, methods, kwargs, firstresult)#x1B[90m#x1B[39;49;00m
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
        firstresult = False
        hook_name  = 'pytest_runtest_setup'
        kwargs     = {'item': <Function test_streaming_11[True]>}
        methods    = [<HookImpl plugin_name='threadexception', plugin=<module '_pytest.threadexception' from '.../work/cheroot/che...pper name='/dev/null' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>>, ...]
        self       = <_pytest.config.PytestPluginManager object at 0xff3dd8c57a10>
#x1B[1m#x1B[31m..../py/lib/python3.14.../site-packages/_pytest/logging.py#x1B[0m:843: in pytest_runtest_setup
    #x1B[0m#x1B[94myield#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        empty      = {'setup': []}
        item       = <Function test_streaming_11[True]>
        self       = <_pytest.logging.LoggingPlugin object at 0xff3dd3859550>
#x1B[1m#x1B[31m..../py/lib/python3.14.../site-packages/_pytest/capture.py#x1B[0m:895: in pytest_runtest_setup
    #x1B[0m#x1B[94mreturn#x1B[39;49;00m (#x1B[94myield#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
            ^^^^^#x1B[90m#x1B[39;49;00m
        item       = <Function test_streaming_11[True]>
        self       = <CaptureManager _method='fd' _global_capturing=<MultiCapture out=<FDCapture 1 oldfd=7 _state='suspended' tmpfile=<Enco...xtIOWrapper name='/dev/null' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>
#x1B[1m#x1B[31m..../py/lib/python3.14........./site-packages/_pytest/unraisableexception.py#x1B[0m:148: in pytest_runtest_setup
    #x1B[0mcollect_unraisable(item.config)#x1B[90m#x1B[39;49;00m
        item       = <Function test_streaming_11[True]>
#x1B[1m#x1B[31m..../py/lib/python3.14........./site-packages/_pytest/unraisableexception.py#x1B[0m:77: in collect_unraisable
    #x1B[0m#x1B[94mraise#x1B[39;49;00m errors[#x1B[94m0#x1B[39;49;00m]#x1B[90m#x1B[39;49;00m
        config     = <_pytest.config.Config object at 0xff3dd8a45010>
        msg        = 'Exception ignored while calling deallocator <function IOBase.__del__ at 0xff3dd38da400>: None\n\nTraceback (most rece...43, in write\n    return self._sock.send(b)\n           ~~~~~~~~~~~~~~~^^^\nOSError: [Errno 9] Bad file descriptor\n\n'
        pop_unraisable = <built-in method pop of collections.deque object at 0xff3dd39a7010>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

config = <_pytest.config.Config object at 0xff3dd8a45010>

    #x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mcollect_unraisable#x1B[39;49;00m(config: Config) -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
        pop_unraisable = config.stash[unraisable_exceptions].pop#x1B[90m#x1B[39;49;00m
        errors: #x1B[96mlist#x1B[39;49;00m[pytest.PytestUnraisableExceptionWarning | #x1B[96mRuntimeError#x1B[39;49;00m] = []#x1B[90m#x1B[39;49;00m
        meta = #x1B[94mNone#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        hook_error = #x1B[94mNone#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
            #x1B[94mwhile#x1B[39;49;00m #x1B[94mTrue#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
                #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
                    meta = pop_unraisable()#x1B[90m#x1B[39;49;00m
                #x1B[94mexcept#x1B[39;49;00m #x1B[96mIndexError#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
                    #x1B[94mbreak#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
                #x1B[94mif#x1B[39;49;00m #x1B[96misinstance#x1B[39;49;00m(meta, #x1B[96mBaseException#x1B[39;49;00m):#x1B[90m#x1B[39;49;00m
                    hook_error = #x1B[96mRuntimeError#x1B[39;49;00m(#x1B[33m"#x1B[39;49;00m#x1B[33mFailed to process unraisable exception#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
                    hook_error.__cause__ = meta#x1B[90m#x1B[39;49;00m
                    errors.append(hook_error)#x1B[90m#x1B[39;49;00m
                    #x1B[94mcontinue#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
                msg = meta.msg#x1B[90m#x1B[39;49;00m
                #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
>                   warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE                   pytest.PytestUnraisableExceptionWarning: Exception ignored while calling deallocator <function IOBase.__del__ at 0xff3dd38da400>: None#x1B[0m

config     = <_pytest.config.Config object at 0xff3dd8a45010>
msg        = 'Exception ignored while calling deallocator <function IOBase.__del__ at 0xff3dd38da400>: None\n\nTraceback (most rece...43, in write\n    return self._sock.send(b)\n           ~~~~~~~~~~~~~~~^^^\nOSError: [Errno 9] Bad file descriptor\n\n'
pop_unraisable = <built-in method pop of collections.deque object at 0xff3dd39a7010>

#x1B[1m#x1B[31m..../py/lib/python3.14........./site-packages/_pytest/unraisableexception.py#x1B[0m:65: PytestUnraisableExceptionWarning
cheroot.test.test_makefile::test_bytes_written
Stack Traces | 0s run time
self = <cheroot.makefile.StreamWriter name=-1>

    #x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92m__del__#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m):#x1B[90m#x1B[39;49;00m
    #x1B[90m    #x1B[39;49;00m#x1B[33m"""Destructor.  Calls close()."""#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
            closed = #x1B[96mself#x1B[39;49;00m.closed#x1B[90m#x1B[39;49;00m
        #x1B[94mexcept#x1B[39;49;00m #x1B[96mAttributeError#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
            #x1B[90m# If getting closed fails, then the object is probably#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            #x1B[90m# in an unusable state, so ignore.#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            #x1B[94mreturn#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[94mif#x1B[39;49;00m closed:#x1B[90m#x1B[39;49;00m
            #x1B[94mreturn#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[94mif#x1B[39;49;00m dealloc_warn := #x1B[96mgetattr#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33m_dealloc_warn#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mNone#x1B[39;49;00m):#x1B[90m#x1B[39;49;00m
            dealloc_warn(#x1B[96mself#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[90m# If close() fails, the caller logs the exception with#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[90m# sys.unraisablehook. close() must be called at the end at __del__().#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
>       #x1B[96mself#x1B[39;49;00m.close()#x1B[90m#x1B[39;49;00m

closed     = False
dealloc_warn = <bound method _BufferedIOMixin._dealloc_warn of <cheroot.makefile.StreamWriter name=-1>>
self       = <cheroot.makefile.StreamWriter name=-1>

#x1B[1m#x1B[.../Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/_pyio.py#x1B[0m:415: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
#x1B[1m#x1B[.../Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/_pyio.py#x1B[0m:1313: in close
    #x1B[0m#x1B[96mself#x1B[39;49;00m.raw.close()#x1B[90m#x1B[39;49;00m
        self       = <cheroot.makefile.StreamWriter name=-1>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <socket.SocketIO object at 0x1047ebee0>

    #x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mclose#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m):#x1B[90m#x1B[39;49;00m
    #x1B[90m    #x1B[39;49;00m#x1B[33m"""Close the SocketIO object.  This doesn't close the underlying#x1B[39;49;00m
    #x1B[33m    socket, except if all references to it have disappeared.#x1B[39;49;00m
    #x1B[33m    """#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[94mif#x1B[39;49;00m #x1B[96mself#x1B[39;49;00m.closed:#x1B[90m#x1B[39;49;00m
            #x1B[94mreturn#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        io.RawIOBase.close(#x1B[96mself#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
>       #x1B[96mself#x1B[39;49;00m._sock._decref_socketios()#x1B[90m#x1B[39;49;00m
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE       AttributeError: 'MockSocket' object has no attribute '_decref_socketios'#x1B[0m

self       = <socket.SocketIO object at 0x1047ebee0>

#x1B[1m#x1B[.../Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/socket.py#x1B[0m:795: AttributeError

#x1B[33mThe above exception was the direct cause of the following exception:#x1B[0m

cls = <class '_pytest.runner.CallInfo'>
func = <function call_and_report.<locals>.<lambda> at 0x104a21e80>
when = 'call'
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)

    #x1B[0m#x1B[37m@classmethod#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mfrom_call#x1B[39;49;00m(#x1B[90m#x1B[39;49;00m
        #x1B[96mcls#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
        func: Callable[[], TResult],#x1B[90m#x1B[39;49;00m
        when: Literal[#x1B[33m"#x1B[39;49;00m#x1B[33mcollect#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33msetup#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mcall#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mteardown#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m],#x1B[90m#x1B[39;49;00m
        reraise: #x1B[96mtype#x1B[39;49;00m[#x1B[96mBaseException#x1B[39;49;00m] | #x1B[96mtuple#x1B[39;49;00m[#x1B[96mtype#x1B[39;49;00m[#x1B[96mBaseException#x1B[39;49;00m], ...] | #x1B[94mNone#x1B[39;49;00m = #x1B[94mNone#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
    ) -> CallInfo[TResult]:#x1B[90m#x1B[39;49;00m
    #x1B[90m    #x1B[39;49;00m#x1B[33m"""Call func, wrapping the result in a CallInfo.#x1B[39;49;00m
    #x1B[33m#x1B[39;49;00m
    #x1B[33m    :param func:#x1B[39;49;00m
    #x1B[33m        The function to call. Called without arguments.#x1B[39;49;00m
    #x1B[33m    :type func: Callable[[], _pytest.runner.TResult]#x1B[39;49;00m
    #x1B[33m    :param when:#x1B[39;49;00m
    #x1B[33m        The phase in which the function is called.#x1B[39;49;00m
    #x1B[33m    :param reraise:#x1B[39;49;00m
    #x1B[33m        Exception or exceptions that shall propagate if raised by the#x1B[39;49;00m
    #x1B[33m        function, instead of being wrapped in the CallInfo.#x1B[39;49;00m
    #x1B[33m    """#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        excinfo = #x1B[94mNone#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        instant = timing.Instant()#x1B[90m#x1B[39;49;00m
        #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
>           result: TResult | #x1B[94mNone#x1B[39;49;00m = func()#x1B[90m#x1B[39;49;00m
                                     ^^^^^^#x1B[90m#x1B[39;49;00m

cls        = <class '_pytest.runner.CallInfo'>
duration   = Duration(start=Instant(time=1750178839.521895, perf_count=1117.486149416), stop=Instant(time=1750178839.52235, perf_count=1117.486604458))
excinfo    = <ExceptionInfo PytestUnraisableExceptionWarning('Exception ignored while calling deallocator <function IOBase.__del__ at 0x1056880f0>: None\n') tblen=19>
func       = <function call_and_report.<locals>.<lambda> at 0x104a21e80>
instant    = Instant(time=1750178839.521895, perf_count=1117.486149416)
reraise    = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)
result     = None
when       = 'call'

#x1B[1m#x1B[31m..../py/lib/python3.14....../site-packages/_pytest/runner.py#x1B[0m:344: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
#x1B[1m#x1B[31m..../py/lib/python3.14....../site-packages/_pytest/runner.py#x1B[0m:246: in <lambda>
    #x1B[0m#x1B[94mlambda#x1B[39;49;00m: runtest_hook(item=item, **kwds), when=when, reraise=reraise#x1B[90m#x1B[39;49;00m
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
        item       = <Function test_bytes_written>
        kwds       = {}
        runtest_hook = <HookCaller 'pytest_runtest_call'>
#x1B[1m#x1B[31m..../py/lib/python3.14.../site-packages/pluggy/_hooks.py#x1B[0m:512: in __call__
    #x1B[0m#x1B[94mreturn#x1B[39;49;00m #x1B[96mself#x1B[39;49;00m._hookexec(#x1B[96mself#x1B[39;49;00m.name, #x1B[96mself#x1B[39;49;00m._hookimpls.copy(), kwargs, firstresult)#x1B[90m#x1B[39;49;00m
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
        firstresult = False
        kwargs     = {'item': <Function test_bytes_written>}
        self       = <HookCaller 'pytest_runtest_call'>
#x1B[1m#x1B[31m..../py/lib/python3.14.../site-packages/pluggy/_manager.py#x1B[0m:120: in _hookexec
    #x1B[0m#x1B[94mreturn#x1B[39;49;00m #x1B[96mself#x1B[39;49;00m._inner_hookexec(hook_name, methods, kwargs, firstresult)#x1B[90m#x1B[39;49;00m
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
        firstresult = False
        hook_name  = 'pytest_runtest_call'
        kwargs     = {'item': <Function test_bytes_written>}
        methods    = [<HookImpl plugin_name='threadexception', plugin=<module '_pytest.threadexception' from '.../work/cheroot/ch...capture_fixture=None>>, <HookImpl plugin_name='_cov', plugin=<pytest_cov.plugin.CovPlugin object at 0x105565a90>>, ...]
        self       = <_pytest.config.PytestPluginManager object at 0x1048038c0>
#x1B[1m#x1B[31m..../py/lib/python3.14.../site-packages/_pytest/logging.py#x1B[0m:850: in pytest_runtest_call
    #x1B[0m#x1B[94myield#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        item       = <Function test_bytes_written>
        self       = <_pytest.logging.LoggingPlugin object at 0x105915550>
#x1B[1m#x1B[31m..../py/lib/python3.14....../site-packages/pluggy/_callers.py#x1B[0m:53: in run_old_style_hookwrapper
    #x1B[0m#x1B[94mreturn#x1B[39;49;00m result.get_result()#x1B[90m#x1B[39;49;00m
           ^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
        args       = [<Function test_bytes_written>]
        hook_impl  = <HookImpl plugin_name='_cov', plugin=<pytest_cov.plugin.CovPlugin object at 0x105565a90>>
        hook_name  = 'pytest_runtest_call'
        result     = <pluggy._result.Result object at 0x106e8a6c0>
        teardown   = <generator object CovPlugin.pytest_runtest_call at 0x1067f4040>
#x1B[1m#x1B[31m..../py/lib/python3.14....../site-packages/pluggy/_callers.py#x1B[0m:38: in run_old_style_hookwrapper
    #x1B[0mres = #x1B[94myield#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
          ^^^^^#x1B[90m#x1B[39;49;00m
        args       = [<Function test_bytes_written>]
        hook_impl  = <HookImpl plugin_name='_cov', plugin=<pytest_cov.plugin.CovPlugin object at 0x105565a90>>
        hook_name  = 'pytest_runtest_call'
        result     = <pluggy._result.Result object at 0x106e8a6c0>
        teardown   = <generator object CovPlugin.pytest_runtest_call at 0x1067f4040>
#x1B[1m#x1B[31m..../py/lib/python3.14.../site-packages/_pytest/capture.py#x1B[0m:900: in pytest_runtest_call
    #x1B[0m#x1B[94mreturn#x1B[39;49;00m (#x1B[94myield#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
            ^^^^^#x1B[90m#x1B[39;49;00m
        item       = <Function test_bytes_written>
        self       = <CaptureManager _method='fd' _global_capturing=<MultiCapture out=<FDCapture 1 oldfd=7 _state='suspended' tmpfile=<Enco...xtIOWrapper name='/dev/null' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>
#x1B[1m#x1B[31m..../py/lib/python3.14.../site-packages/_pytest/skipping.py#x1B[0m:263: in pytest_runtest_call
    #x1B[0m#x1B[94mreturn#x1B[39;49;00m (#x1B[94myield#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
            ^^^^^#x1B[90m#x1B[39;49;00m
        item       = <Function test_bytes_written>
        xfailed    = None
#x1B[1m#x1B[31m..../py/lib/python3.14........./site-packages/_pytest/unraisableexception.py#x1B[0m:153: in pytest_runtest_call
    #x1B[0mcollect_unraisable(item.config)#x1B[90m#x1B[39;49;00m
        item       = <Function test_bytes_written>
#x1B[1m#x1B[31m..../py/lib/python3.14........./site-packages/_pytest/unraisableexception.py#x1B[0m:77: in collect_unraisable
    #x1B[0m#x1B[94mraise#x1B[39;49;00m errors[#x1B[94m0#x1B[39;49;00m]#x1B[90m#x1B[39;49;00m
        config     = <_pytest.config.Config object at 0x104960ec0>
        msg        = "Exception ignored while calling deallocator <function IOBase.__del__ at 0x1056880f0>: None\n\nTraceback (most recent ...tios()\n    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAttributeError: 'MockSocket' object has no attribute '_decref_socketios'\n\n"
        pop_unraisable = <built-in method pop of collections.deque object at 0x105862e30>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

config = <_pytest.config.Config object at 0x104960ec0>

    #x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mcollect_unraisable#x1B[39;49;00m(config: Config) -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
        pop_unraisable = config.stash[unraisable_exceptions].pop#x1B[90m#x1B[39;49;00m
        errors: #x1B[96mlist#x1B[39;49;00m[pytest.PytestUnraisableExceptionWarning | #x1B[96mRuntimeError#x1B[39;49;00m] = []#x1B[90m#x1B[39;49;00m
        meta = #x1B[94mNone#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        hook_error = #x1B[94mNone#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
            #x1B[94mwhile#x1B[39;49;00m #x1B[94mTrue#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
                #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
                    meta = pop_unraisable()#x1B[90m#x1B[39;49;00m
                #x1B[94mexcept#x1B[39;49;00m #x1B[96mIndexError#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
                    #x1B[94mbreak#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
                #x1B[94mif#x1B[39;49;00m #x1B[96misinstance#x1B[39;49;00m(meta, #x1B[96mBaseException#x1B[39;49;00m):#x1B[90m#x1B[39;49;00m
                    hook_error = #x1B[96mRuntimeError#x1B[39;49;00m(#x1B[33m"#x1B[39;49;00m#x1B[33mFailed to process unraisable exception#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
                    hook_error.__cause__ = meta#x1B[90m#x1B[39;49;00m
                    errors.append(hook_error)#x1B[90m#x1B[39;49;00m
                    #x1B[94mcontinue#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
                msg = meta.msg#x1B[90m#x1B[39;49;00m
                #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
>                   warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE                   pytest.PytestUnraisableExceptionWarning: Exception ignored while calling deallocator <function IOBase.__del__ at 0x1056880f0>: None#x1B[0m

config     = <_pytest.config.Config object at 0x104960ec0>
msg        = "Exception ignored while calling deallocator <function IOBase.__del__ at 0x1056880f0>: None\n\nTraceback (most recent ...tios()\n    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAttributeError: 'MockSocket' object has no attribute '_decref_socketios'\n\n"
pop_unraisable = <built-in method pop of collections.deque object at 0x105862e30>

#x1B[1m#x1B[31m..../py/lib/python3.14........./site-packages/_pytest/unraisableexception.py#x1B[0m:65: PytestUnraisableExceptionWarning

To view more test analytics, go to the Test Analytics Dashboard
πŸ“‹ Got 3 mins? Take this short survey to help us improve Test Analytics.

@itamarst itamarst marked this pull request as draft May 23, 2025 13:29
@itamarst itamarst force-pushed the 503-when-overloaded branch 2 times, most recently from 05f2bee to 57206a1 Compare May 23, 2025 13:47
@itamarst itamarst force-pushed the 503-when-overloaded branch 3 times, most recently from ead7721 to 97a6317 Compare May 28, 2025 17:24
@itamarst
Copy link
Author

So now it ... fails, but not as consistently? I don't really know.

@itamarst
Copy link
Author

Any other thoughts? It's pretty annoying that it passes some of the time but not all the time.

@itamarst itamarst force-pushed the 503-when-overloaded branch from 45698b9 to 4a5abc5 Compare June 2, 2025 15:54
@itamarst itamarst marked this pull request as ready for review June 2, 2025 16:05
@itamarst
Copy link
Author

itamarst commented Jun 2, 2025

Looks like we're down to only 3.14 failing, which were already failing on main so hopefully unrelated to my changes.


def test_overload_results_in_suitable_http_error(request):
"""A server that can't keep up with requests returns a 503 HTTP error."""
localhost = '127.0.0.1'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be possible to import a constant with this from somewhere.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't find it.

def serve(self):
"""Serve requests, after invoking :func:`prepare()`."""
threading.Thread(target=self._serve_unservicable).start()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to wait for the thread to complete somewhere?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... Probably not? other way around, it's possible it should be a daemon thread if you don't want sending 503s to delay shutdown of a process.

def _serve_unservicable(self):
"""Serve connections we can't handle a 503."""
while self.ready:
conn = self._unservicable_conns.get()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need a timeout?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming stop() is always used, then no.

while self.ready:
conn = self._unservicable_conns.get()
if conn is None:
return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should call shutdown() here (under Python 3.13+).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you clarify what you mean?

level=logging.ERROR,
traceback=True,
)
conn.linger = True
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just copying existing code that did that in error handling, but empirically looks like not on Linux at least. Will see what CI does on Windows and macOS.

request = HTTPRequest(self, conn)
try:
request.simple_response('503 Service Unavailable')
except Exception as ex:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to make this only intercept narrow exceptions?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a catchall so the thread doesn't die. But I can make more likely exceptions not get logged, at least.

@itamarst itamarst force-pushed the 503-when-overloaded branch from c532189 to b460ce4 Compare June 17, 2025 16:41
@itamarst
Copy link
Author

itamarst commented Jun 17, 2025

Failures are due to 3.14 and #747, unrelated to this PR as far as I can tell.

@itamarst itamarst requested a review from webknjaz June 17, 2025 17:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants