-
Notifications
You must be signed in to change notification settings - Fork 212
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2d9485f
commit 152040a
Showing
5 changed files
with
100 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import asyncio | ||
import logging | ||
import weakref | ||
|
||
import aiohttp | ||
|
||
from conf.asgi import application | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
_SESSIONS: weakref.WeakKeyDictionary[ | ||
asyncio.AbstractEventLoop, aiohttp.ClientSession | ||
] = weakref.WeakKeyDictionary() | ||
|
||
_LOCKS: weakref.WeakKeyDictionary[ | ||
asyncio.AbstractEventLoop, asyncio.Lock | ||
] = weakref.WeakKeyDictionary() | ||
|
||
|
||
async def get_aiohttp_session() -> aiohttp.ClientSession: | ||
""" | ||
Safely retrieve a shared aiohttp session for the current event loop. | ||
If the loop already has an aiohttp session associated, it will be reused. | ||
If the loop has not yet had an aiohttp session created for it, a new one | ||
will be created and returned. | ||
While the main application will always run in the same loop, and while | ||
that covers 99% of our use cases, it is still possible for `async_to_sync` | ||
to cause a new loop to be created if, for example, `force_new_loop` is | ||
passed. In order to prevent surprises should that ever be the case, this | ||
function assumes that it's possible for multiple loops to be present in | ||
the lifetime of the application and therefore we need to verify that each | ||
loop gets its own session. | ||
""" | ||
|
||
loop = asyncio.get_running_loop() | ||
|
||
if loop not in _LOCKS: | ||
_LOCKS[loop] = asyncio.Lock() | ||
|
||
async with _LOCKS[loop]: | ||
if loop not in _SESSIONS: | ||
create_session = True | ||
msg = "No session for loop. Creating new session." | ||
elif _SESSIONS[loop].closed: | ||
create_session = True | ||
msg = "Loop's previous session closed. Creating new session." | ||
else: | ||
create_session = False | ||
msg = "Reusing existing session for loop." | ||
|
||
logger.info(msg) | ||
|
||
if create_session: | ||
session = aiohttp.ClientSession() | ||
application.register_shutdown_handler(session.close) | ||
_SESSIONS[loop] = session | ||
|
||
return _SESSIONS[loop] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import pytest | ||
from asgiref.sync import async_to_sync | ||
|
||
from conf.asgi import application | ||
|
||
|
||
@pytest.fixture(scope="session", autouse=True) | ||
def call_application_shutdown(): | ||
""" | ||
Call application shutdown during test session teardown. | ||
This cannot be an async fixture because the scope is session | ||
and pytest-asynio's `event_loop` fixture, which is auto-used | ||
for async tests and fixtures, is function scoped, which is | ||
incomatible with session scoped fixtures. `async_to_sync` works | ||
fine here, so it's not a problem. | ||
""" | ||
yield | ||
async_to_sync(application.shutdown)() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters