-
-
Notifications
You must be signed in to change notification settings - Fork 353
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
Bug: declaring a RedisStore in the app triggers a 500 error in the 2nd set of parameters of a parametrized test #3083
Comments
thinking out loud after I read this, shouldn't there be some close/aclose methods for stores, or at least one for the RedisStore ? |
this can be circumvented in the get_app factory replacing the |
I agree that we should add this to the stores. Then Litestar could close the stores on app shutdown (make it configurable as to whether the store should be closed or not). |
I greped all python Redis store in github and nobody seems to have methods to close stores weirdly enough, so we're not alone :) |
The reason this does not exist is that the store can receive an "externally managed" Maybe the solution here is to add a flag to the
There are/were quite a few other issues surrounding the async Redis client and lifetimes, e.g. it trying to perform some action in You could try though to manage its lifetime explicitly and see if that fixes the issue. |
something like this does work indeed, I'm not too sure this is not overly complicated though it does the job, and my app factory needs to be async now, and I need to wrap it in an async context manager that creates / closes the redis client : from contextlib import asynccontextmanager
from typing import Iterator
import msgspec
import pytest
from redis.asyncio import Redis, ConnectionPool
from litestar import Litestar, get
from litestar.middleware.session.server_side import ServerSideSessionConfig
from litestar.testing import AsyncTestClient
@get()
async def hget() -> int:
return 1
class AppSettings(msgspec.Struct):
debug: bool
redis_url: str = "redis://localhost:6379"
@asynccontextmanager
async def make_session_store(app_settings: AppSettings) -> Iterator[None]:
pool = ConnectionPool.from_url(app_settings.redis_url)
client = Redis.from_pool(pool)
try:
yield
finally:
await client.aclose()
async def get_app(app_settings: AppSettings) -> Litestar:
# setting up stores
# session_store = RedisStore.with_client(url=app_settings.redis_url)
# session_store = RedisStore(redis=Redis(), namespace="sessions")
# session_store = MemoryStore()
session_config = ServerSideSessionConfig()
async with make_session_store(app_settings) as session_store:
app = Litestar(route_handlers=[hget],
middleware=[
session_config.middleware,
],
stores={"sessions": session_store}, # comment this and 2nd test pass
debug=app_settings.debug,
# lifespan=[partial(lifespan, app_settings=app_settings)]
)
return app
@pytest.fixture(scope="session")
def anyio_backend() -> str:
return "asyncio"
@pytest.fixture(scope="session")
def app_settings_test():
return AppSettings(debug=True)
@pytest.fixture(scope="session")
async def app_test(app_settings_test: AppSettings):
app = await get_app(app_settings_test)
yield app
@pytest.fixture # add scope="session" and 2nd test pass
async def client(app_test: Litestar):
async with AsyncTestClient(app=app_test) as c:
yield c
@pytest.mark.anyio(scope="session")
@pytest.mark.parametrize("p1, p2",
[(1, 2), (3, 4)])
async def test_param(client: AsyncTestClient, p1: int, p2: int):
response = await client.get("/")
assert response.status_code == 200 |
actually maybe if there is a way to lazily load the store, or use a lifespan-created redis client into the store that would be maybe cleaner, not sure... |
I think you should be able to create the store in a sync function and then have a lifespan context manager on the app instance that takes care of its shutdown. |
alright yeah this is much better this way indeed, I just have to access the private redis attribute from the store in the lifespan in order to close it but that solves everything and looks not ugly like the above :) thanks for the patience )
Do you think I should maybe try "solve" this adding a note in the docs ? This is not trivial and as you said maybe unexpected at first. lmk
|
I think there are two things that need to be done:
|
sounds good to me, I'm keen on tackling both, 1st one will be simpler and faster obviously |
I've assigned you here. I'll leave it up to you if you want to address both in a single PR or two separate ones, both are fine with me. |
A fix for this issue has been released in v2.6.2 |
Description
If you have a parametrized test that uses a function-scoped fixture that uses itself a session-scoped fixture and you declare a RedisStore in the app, the 2nd test fails with
RuntimeError: Event loop is closed
The 2nd test passes in those cases:
URL to code causing the issue
No response
MCVE
Steps to reproduce
Screenshots
"![SCREENSHOT_DESCRIPTION](SCREENSHOT_LINK.png)"
Logs
No response
Litestar Version
2.5.5
Platform
Note
While we are open for sponsoring on GitHub Sponsors and
OpenCollective, we also utilize Polar.sh to engage in pledge-based sponsorship.
Check out all issues funded or available for funding on our Polar.sh dashboard
The text was updated successfully, but these errors were encountered: