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

Using eager task factory with aiohttp 3.10.0 and cpython 3.12+ raise an AssertionError in stdlib asyncio.staggered (fixed in aiohappyeyeballs 2.4.3+) #8599

Open
1 task done
2-5 opened this issue Aug 3, 2024 · 42 comments
Labels

Comments

@2-5
Copy link

2-5 commented Aug 3, 2024

Describe the bug

discord.py 2.4.0 throws exception after upgrading aiohttp to 3.10.0:

To Reproduce

  1. use discord.py 2.4.0

Expected behavior

no exception

Logs/tracebacks

Traceback (most recent call last):
  File ".venv/lib/python3.12/site-packages/discord/client.py", line 786, in start
    await self.login(token)
  File ".venv/lib/python3.12/site-packages/discord/client.py", line 620, in login
    data = await self.http.static_login(token)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".venv/lib/python3.12/site-packages/discord/http.py", line 816, in static_login
    data = await self.request(Route('GET', '/users/@me'))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".venv/lib/python3.12/site-packages/discord/http.py", line 638, in request
    async with self.__session.request(method, url, **kwargs) as response:
  File ".venv/lib/python3.12/site-packages/aiohttp/client.py", line 1344, in __aenter__
    self._resp = await self._coro
                 ^^^^^^^^^^^^^^^^
  File ".venv/lib/python3.12/site-packages/aiohttp/client.py", line 648, in _request
    conn = await self._connector.connect(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".venv/lib/python3.12/site-packages/aiohttp/connector.py", line 546, in connect
    proto = await self._create_connection(req, traces, timeout)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".venv/lib/python3.12/site-packages/aiohttp/connector.py", line 954, in _create_connection
    _, proto = await self._create_direct_connection(req, traces, timeout)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".venv/lib/python3.12/site-packages/aiohttp/connector.py", line 1282, in _create_direct_connection
    transp, proto = await self._wrap_create_connection(
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".venv/lib/python3.12/site-packages/aiohttp/connector.py", line 1036, in _wrap_create_connection
    sock = await aiohappyeyeballs.start_connection(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".venv/lib/python3.12/site-packages/aiohappyeyeballs/impl.py", line 89, in start_connection
    sock, _, _ = await staggered.staggered_race(
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/asyncio/staggered.py", line 144, in staggered_race
    raise d.exception()
  File "/usr/lib/python3.12/asyncio/staggered.py", line 101, in run_one_coro
    assert len(running_tasks) == this_index + 2
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError

Python Version

$ python --version
Python 3.12.4

aiohttp Version

$ python -m pip show aiohttp
Name:                  aiohttp                                                                            
Latest version:        3.10.0                                                                             
Latest stable version: 3.10.0                                                                             
Installed version:     3.10.0                                                                             
Summary:               Async http client/server framework (asyncio)                                       
Requires Python:       >=3.8                                                                              
Author:                                                                                                   
Author email:                                                                                             
License:               Apache 2                                                                           
Homepage:              https://github.com/aio-libs/aiohttp                                                
Project URLs:          Chat: Matrix: https://matrix.to/#/#aio-libs:matrix.org                             
                       Chat: Matrix Space: https://matrix.to/#/#aio-libs-space:matrix.org                 
                       CI: GitHub Actions: https://github.com/aio-libs/aiohttp/actions?query=workflow%3ACI
                       Coverage: codecov: https://codecov.io/github/aio-libs/aiohttp                      
                       Docs: Changelog: https://docs.aiohttp.org/en/stable/changes.html                   
                       Docs: RTD: https://docs.aiohttp.org                                                
                       GitHub: issues: https://github.com/aio-libs/aiohttp/issues                         
                       GitHub: repo: https://github.com/aio-libs/aiohttp                                  
Platform:                                                                                                 
Keywords:

multidict Version

$ python -m pip show multidict
Name:                  multidict                                                      
Latest version:        6.0.5                                                          
Latest stable version: 6.0.5                                                          
Installed version:     6.0.5                                                          
Summary:               multidict implementation                                       
Requires Python:       >=3.7                                                          
Author:                Andrew Svetlov                                                 
Author email:          andrew.svetlov@gmail.com                                       
License:               Apache 2                                                       
Homepage:              https://github.com/aio-libs/multidict                          
Project URLs:          Chat: Gitter: https://gitter.im/aio-libs/Lobby                 
                       CI: GitHub: https://github.com/aio-libs/multidict/actions      
                       Coverage: codecov: https://codecov.io/github/aio-libs/multidict
                       Docs: RTD: https://multidict.aio-libs.org                      
                       GitHub: issues: https://github.com/aio-libs/multidict/issues   
                       GitHub: repo: https://github.com/aio-libs/multidict            
Platform:                                                                             
Keywords:

yarl Version

$ python -m pip show yarl
Name:                  yarl                                                                               
Latest version:        1.9.4                                                                              
Latest stable version: 1.9.4                                                                              
Installed version:     1.9.4                                                                              
Summary:               Yet another URL library                                                            
Requires Python:       >=3.7                                                                              
Author:                Andrew Svetlov                                                                     
Author email:          andrew.svetlov@gmail.com                                                           
License:               Apache-2.0                                                                         
Homepage:              https://github.com/aio-libs/yarl                                                   
Project URLs:          Chat: Matrix: https://matrix.to/#/#aio-libs:matrix.org                             
                       Chat: Matrix Space: https://matrix.to/#/#aio-libs-space:matrix.org                 
                       CI: GitHub Workflows: https://github.com/aio-libs/yarl/actions?query=branch:master 
                       Code of Conduct: https://github.com/aio-libs/.github/blob/master/CODE_OF_CONDUCT.md
                       Coverage: codecov: https://codecov.io/github/aio-libs/yarl                         
                       Docs: Changelog: https://yarl.aio-libs.org/en/latest/changes/                      
                       Docs: RTD: https://yarl.aio-libs.org                                               
                       GitHub: issues: https://github.com/aio-libs/yarl/issues                            
                       GitHub: repo: https://github.com/aio-libs/yarl                                     
Platform:                                                                                                 
Keywords:              cython, cext, yarl

OS

Ubuntu 22.04

Related component

Client

Additional context

No response

Code of Conduct

  • I agree to follow the aio-libs Code of Conduct
@2-5 2-5 added the bug label Aug 3, 2024
@bdraco bdraco added this to the 3.10.2 milestone Aug 3, 2024
@bdraco
Copy link
Member

bdraco commented Aug 3, 2024

This looks like a bug in stdlib asyncio.staggered

Have you had a chance to look through python’s issue tracker?

@bdraco
Copy link
Member

bdraco commented Aug 3, 2024

Are you by chance using an eager task factory?

@2-5
Copy link
Author

2-5 commented Aug 3, 2024

Yes, I'm using eager task factory:

loop = asyncio.get_running_loop()
loop.set_task_factory(asyncio.eager_task_factory)

aiohttp 3.9.5 worked fine with the same Python 3.12 and the eager task factory, 3.10 is triggering the bug.

@bdraco
Copy link
Member

bdraco commented Aug 3, 2024

It looks like asyncio.staggered is broken with eager task factory because it assumes the task will not run immediately. Would you please open a bug report for cpython?

@bdraco
Copy link
Member

bdraco commented Aug 3, 2024

A workaround would be to turn off happyeyeballs in the connector until cpython is fixed when using eager task factory.

happy_eyeballs_delay can be set to None on the connector to disable happyeyeballs which will workaround the bug in cpython asyncio.staggered when using it with eager task factory in the mean time.

@bdraco bdraco changed the title discord.py fails after upgrade to 3.10.0 discord.py fails after upgrade to 3.10.0 with cpython 3.12+ using an eager task factory Aug 4, 2024
@bdraco bdraco changed the title discord.py fails after upgrade to 3.10.0 with cpython 3.12+ using an eager task factory Using eager task factory with aiohttp 3.10.0 and cpython 3.12+ raise an AssertionError in stdlib asyncio.staggered Aug 4, 2024
@bdraco bdraco added the waiting-for-upstream We are waiting for an upstream change label Aug 7, 2024
@bdraco
Copy link
Member

bdraco commented Aug 7, 2024

@2-5 I'd like to get this fixed in upstream cpython. Would you please open an issue there?

@bdraco bdraco modified the milestones: 3.10.2, 3.10.3 Aug 7, 2024
@yuvalshi0
Copy link

We seem to have a somewhat related issue, only on python 3.11.9 (aiohttp 3.10.1)

Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/aiobotocore/httpsession.py", line 208, in send
response = await self._session.request(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/aiohttp/client.py", line 648, in _request
conn = await self._connector.connect(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/aiohttp/connector.py", line 546, in connect
proto = await self._create_connection(req, traces, timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/aiohttp/connector.py", line 954, in _create_connection
_, proto = await self._create_direct_connection(req, traces, timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/aiohttp/connector.py", line 1282, in _create_direct_connection
transp, proto = await self._wrap_create_connection(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/aiohttp/connector.py", line 1036, in _wrap_create_connection
sock = await aiohappyeyeballs.start_connection(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/aiohappyeyeballs/impl.py", line 89, in start_connection
sock, _, _ = await staggered.staggered_race(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/asyncio/staggered.py", line 144, in staggered_race
raise d.exception()
File "/usr/local/lib/python3.11/asyncio/staggered.py", line 116, in run_one_coro
assert winner_index is None
^^^^^^^^^^^^^^^^^^^^
AssertionError

@2-5
Copy link
Author

2-5 commented Aug 8, 2024

@bdraco Sorry, I wouldn't know how to report this bug upstream. I don't know what a staggered_race is, why what we see here is a Python bug and not an aiohttp one, nor what is the expected behavior.

@gediminasel
Copy link

gediminasel commented Sep 17, 2024

Also getting AssertionError from assert winner_index is None but with Python 3.10.12, aiohttp 3.10.3.

UPD: upgrading to Python 3.12.3 solved the issue?

@timborden
Copy link

@gediminasel upgrading to Python 3.12.6 appears to have resolved the assert winner_index is None AssertionError for us as well....FYI

@ZeroIntensity
Copy link

Upstream issue has been fixed, you guys should be able to get this working whenever the next 3.12 patch is released. In the meantime, you can build 3.12 from source to get the fix.

@bdraco
Copy link
Member

bdraco commented Sep 26, 2024

Thank you @ZeroIntensity

@Dreamsorcerer
Copy link
Member

Dreamsorcerer commented Sep 26, 2024

whenever the next 3.12 patch is released

Tuesday: https://peps.python.org/pep-0693/

There's no action for us to take here, right? We can just close this issue.

@bdraco
Copy link
Member

bdraco commented Sep 26, 2024

The end goal is to hopefully merge aiohappyeyeballs back into cpython so we don't have to worry about that anyways as aiohappyeyeballs.start_connection is a subset of loop.create_connection.

Thanks for the fix.

@bdraco bdraco changed the title Using eager task factory with aiohttp 3.10.0 and cpython 3.12+ raise an AssertionError in stdlib asyncio.staggered (fixed in cpython 3.12.7, aiohappyeyeballs 2.4.1) Using eager task factory with aiohttp 3.10.0 and cpython 3.12+ raise an AssertionError in stdlib asyncio.staggered (fixed in cpython 3.12.7, aiohappyeyeballs 2.4.1 OR aiohappyeyeballs 2.4.2+) Sep 27, 2024
@bdraco bdraco reopened this Sep 27, 2024
@bdraco
Copy link
Member

bdraco commented Sep 27, 2024

CI runs are hanging with the new code but on 3.13 only. I have not had time to investigate yet

#9311
#9310
Bug in aiohttp, solved by #9326

Also staggered_race may not handle KeyboardInterrupt and SystemExit well

aio-libs/aiohappyeyeballs#97

@bdraco
Copy link
Member

bdraco commented Sep 28, 2024

It looks like the test failures with the new code and 3.13 are because of a change in the cancellation semantics via python/cpython#117407

@ZeroIntensity
Copy link

Oh boy, that doesn't sound good. This is why it's a bad idea to rely on undocumented APIs. We're working on fixing the backwards compatibility issue via #124700, but as far as I can see, #117407 did not make it into 3.12, so you guys might just have to refactor for 3.13.

If you really want, you can file an issue (and PR, core devs are quite busy with 3.13 imminent) for the change in cancellation.

@bdraco
Copy link
Member

bdraco commented Sep 28, 2024

Thankfully the problem turned out to be a long standing bug in aiohttp which I just fixed in #9326

It looks like the fix in python/cpython#117407 made it more obvious as the cancellation was leaking out of the context manager because aiohttp's helper was never updated for python 3.11+

@ZeroIntensity
Copy link

Great! Please let us know if you find any other problems ASAP so we can get them fixed before the next 3.12 patch is released on Tuesday.

@bdraco
Copy link
Member

bdraco commented Sep 28, 2024

For history the reason we ended up using asyncio.staggered.staggered_race was the discussion in this issue #4451 (comment)

@bdraco
Copy link
Member

bdraco commented Sep 28, 2024

Great! Please let us know if you find any other problems ASAP so we can get them fixed before the next 3.12 patch is released on Tuesday.

The one last loose end is aio-libs/aiohappyeyeballs#97 as it looks like staggered_race doesn't handle SystemExit or KeyboardInterrupt and instead produces RuntimeError: coroutine ignored GeneratorExit

AFAICT this is not a regression in the new code and was present before the refactor.... or my test case is bad.

@ZeroIntensity
Copy link

I'll take a closer look a little later. I'm guessing it's probably that the mock is bad, rather than a CPython problem -- SystemExit seems to be properly raised by staggered_race on my end.

@bdraco
Copy link
Member

bdraco commented Sep 30, 2024

Great! Please let us know if you find any other problems ASAP so we can get them fixed before the next 3.12 patch is released on Tuesday.

@ZeroIntensity looks like there is a race in the new code aio-libs/aiohappyeyeballs#100

@ZeroIntensity
Copy link

It might be worth reverting that fix before the 3.12 patch, it seems to be causing nothing but problems.

@bdraco
Copy link
Member

bdraco commented Sep 30, 2024

@ZeroIntensity Here is a reproducer test for aio-libs/aiohappyeyeballs#100

@pytest.mark.asyncio
async def test_multiple_winners():
    """Test multiple winners are handled correctly."""
    loop = asyncio.get_running_loop()
    winners = []
    finish = loop.create_future()

    async def coro(idx):
        await finish
        winners.append(idx)
        return idx

    coros = [partial(coro, idx) for idx in range(4)]

    task = loop.create_task(staggered_race(coros, delay=0.00001))
    await asyncio.sleep(0.1)
    loop.call_soon(finish.set_result, None)
    winner, index, excs = await task
    assert len(winners) == 4
    assert winners == [0, 1, 2, 3]
    assert winner == 0
    assert index == 0
    assert excs == [None, None, None, None]


@pytest.mark.skipif(sys.version_info < (3, 12), reason="requires python3.12 or higher")
def test_multiple_winners_eager_task_factory():
    """Test multiple winners are handled correctly."""
    loop = asyncio.new_event_loop()
    eager_task_factory = asyncio.create_eager_task_factory(asyncio.Task)
    loop.set_task_factory(eager_task_factory)
    asyncio.set_event_loop(None)

    async def run():
        winners = []
        finish = loop.create_future()

        async def coro(idx):
            await finish
            winners.append(idx)
            return idx

        coros = [partial(coro, idx) for idx in range(4)]

        task = loop.create_task(staggered_race(coros, delay=0.00001))
        await asyncio.sleep(0.1)
        loop.call_soon(finish.set_result, None)
        winner, index, excs = await task
        assert len(winners) == 4
        assert winners == [0, 1, 2, 3]
        assert winner == 0
        assert index == 0
        assert excs == [None, None, None, None]

    loop.run_until_complete(run())

@ZeroIntensity
Copy link

Sorry, I haven't been able to deal with issues today (which is a bit inconvenient considering the patch is tomorrow). I think the best fix here is rolling back the fix for eager task factories and merging your implementation after you've tested it on PyPI.

@bdraco
Copy link
Member

bdraco commented Sep 30, 2024

That sounds like a good plan. I'll followup again in a few weeks once the new implementation has had a chance to prove out in aiohappyeyeballs

@ZeroIntensity
Copy link

Thank you!

@ZeroIntensity
Copy link

FWIW, the revert PR for 3.12.7 is here: python/cpython#124810

This does mean that you won't get support for eager task factories on the prior versions, but that's a tradeoff we have to make here. Hopefully, if your implementation turns out to work properly, we can merge that in to 3.12 and then aiohttp will have things working.

@bdraco
Copy link
Member

bdraco commented Sep 30, 2024

Thank you. I'll see if we can yank aiohappyeyeballs 2.4.2 (not sure it matters now that 2.4.3 is published, but I don't have the right keys to do that). Hopefully everyone will be able to upgrade to aiohappyeyeballs 2.4.3 and the new implementation works out. 🤞

cc @webknjaz

@bdraco bdraco removed this from the 3.10.7 milestone Sep 30, 2024
@bdraco bdraco changed the title Using eager task factory with aiohttp 3.10.0 and cpython 3.12+ raise an AssertionError in stdlib asyncio.staggered (fixed in cpython 3.12.7, aiohappyeyeballs 2.4.1 OR aiohappyeyeballs 2.4.2+) Using eager task factory with aiohttp 3.10.0 and cpython 3.12+ raise an AssertionError in stdlib asyncio.staggered (fixed in aiohappyeyeballs 2.4.3+) Sep 30, 2024
@bdraco bdraco removed the waiting-for-upstream We are waiting for an upstream change label Sep 30, 2024
@ZeroIntensity
Copy link

@bdraco could you see if this PR works with the aiohttp test suite? We want to be much more cautious when merging things related to asyncio.staggered now 🙂

@bdraco
Copy link
Member

bdraco commented Oct 1, 2024

I can test it in production later this week. All my production systems are running the new implementation we shipped in aiohappyeyeballs 2.4.3 and I want to give it a bit more time to bake before switching to testing python/cpython#124847

@bdraco
Copy link
Member

bdraco commented Oct 2, 2024

Home Assistant is shipping the new aiohappyeyeballs today. If all goes well I'll switch over to testing that PR and do some performance comparisons as well

@ZeroIntensity
Copy link

Good to hear!

@bdraco
Copy link
Member

bdraco commented Oct 4, 2024

The new implementation we have in aiohappyeyeballs seems to be going well. No new issues for aiohappyeyeballs filed, Home Assistant release went well, and my testing went well. I think I’m ready to start testing above PR today

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants