Skip to content

Commit 1b11624

Browse files
committed
test: improve resource cleanup in async tests to prevent 'Event loop is closed' error
- Add `close_all_async_resources()` function to properly close all async resources - Update `asyncTearDown()` methods in test classes to use the new cleanup function - Add `tearDownModule()` to ensure async resources are closed after all tests run - Update Python versions in `tests/run.sh` to include 3.8
1 parent d689975 commit 1b11624

File tree

4 files changed

+61
-11
lines changed

4 files changed

+61
-11
lines changed

tests/_catches.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,30 @@ async def close_async_redis_client():
9999

100100

101101
ASYNC_MULTI_CACHES = {
102-
"tlru": RedisFuncCache(__name__, LruTMultiplePolicy, client=ASYNC_REDIS_FACTORY, maxsize=MAXSIZE),
103-
"lru": RedisFuncCache(__name__, LruMultiplePolicy, client=ASYNC_REDIS_FACTORY, maxsize=MAXSIZE),
104-
"mru": RedisFuncCache(__name__, MruMultiplePolicy, client=ASYNC_REDIS_FACTORY, maxsize=MAXSIZE),
105-
"rr": RedisFuncCache(__name__, RrMultiplePolicy, client=ASYNC_REDIS_FACTORY, maxsize=MAXSIZE),
106-
"fifo": RedisFuncCache(__name__, FifoMultiplePolicy, client=ASYNC_REDIS_FACTORY, maxsize=MAXSIZE),
107-
"lfu": RedisFuncCache(__name__, LfuMultiplePolicy, client=ASYNC_REDIS_FACTORY, maxsize=MAXSIZE),
102+
"tlru": RedisFuncCache(__name__, LruTClusterMultiplePolicy, client=ASYNC_REDIS_FACTORY, maxsize=MAXSIZE),
103+
"lru": RedisFuncCache(__name__, LruClusterMultiplePolicy, client=ASYNC_REDIS_FACTORY, maxsize=MAXSIZE),
104+
"mru": RedisFuncCache(__name__, MruClusterMultiplePolicy, client=ASYNC_REDIS_FACTORY, maxsize=MAXSIZE),
105+
"rr": RedisFuncCache(__name__, RrClusterMultiplePolicy, client=ASYNC_REDIS_FACTORY, maxsize=MAXSIZE),
106+
"fifo": RedisFuncCache(__name__, FifoClusterMultiplePolicy, client=ASYNC_REDIS_FACTORY, maxsize=MAXSIZE),
107+
"lfu": RedisFuncCache(__name__, LfuClusterMultiplePolicy, client=ASYNC_REDIS_FACTORY, maxsize=MAXSIZE),
108108
}
109109

110+
111+
async def close_all_async_resources():
112+
"""关闭所有异步资源,防止出现 'Event loop is closed' 错误"""
113+
# 关闭全局异步Redis客户端
114+
await close_async_redis_client()
115+
116+
# 关闭所有异步缓存实例中的客户端连接
117+
for cache in ASYNC_CACHES.values():
118+
if hasattr(cache.client, "close"):
119+
await cache.client.close()
120+
121+
for cache in ASYNC_MULTI_CACHES.values():
122+
if hasattr(cache.client, "close"):
123+
await cache.client.close()
124+
125+
110126
CLUSTER_NODES: List[ClusterNode] = []
111127
CLUSTER_CACHES: Dict[str, RedisFuncCache] = {}
112128
if REDIS_CLUSTER_NODES:

tests/run.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export PIP_ROOT_USER_ACTION=ignore
66
export PIP_NO_WARN_SCRIPT_LOCATION=1
77

88

9-
PYTHON_LIST=(python3.9 python3.10 python3.11 python3.12 python3.13)
9+
PYTHON_LIST=(python3.8 python3.9 python3.10 python3.11 python3.12 python3.13)
1010
for PYTHON in ${PYTHON_LIST[@]}
1111
do
1212
echo

tests/test_async.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from unittest import IsolatedAsyncioTestCase
44
from unittest.mock import AsyncMock, patch
55

6-
from ._catches import ASYNC_CACHES, ASYNC_MULTI_CACHES, CACHES, close_async_redis_client
6+
from ._catches import ASYNC_CACHES, ASYNC_MULTI_CACHES, CACHES, close_all_async_resources
77

88

99
def _echo(x):
@@ -17,7 +17,7 @@ async def asyncSetUp(self):
1717

1818
async def asyncTearDown(self):
1919
# 清理异步Redis客户端连接
20-
await close_async_redis_client()
20+
await asyncio.gather(*(cache.client.close() for cache in ASYNC_CACHES.values()))
2121

2222
async def test_simple(self):
2323
for cache in ASYNC_CACHES.values():
@@ -42,7 +42,7 @@ async def asyncSetUp(self):
4242

4343
async def asyncTearDown(self):
4444
# 清理异步Redis客户端连接
45-
await close_async_redis_client()
45+
await asyncio.gather(*(cache.client.close() for cache in ASYNC_MULTI_CACHES.values()))
4646

4747
async def test_simple(self):
4848
for cache in ASYNC_MULTI_CACHES.values():
@@ -106,3 +106,20 @@ async def test_type_error_sync_for_async(self):
106106
async def _(x):
107107
await asyncio.sleep(0)
108108
return x
109+
110+
111+
# 模块级别的清理函数,在所有测试运行完毕后调用
112+
def tearDownModule():
113+
"""在模块中的所有测试运行完毕后清理异步资源"""
114+
try:
115+
# 获取当前事件循环
116+
loop = asyncio.get_event_loop()
117+
# 如果事件循环还在运行,则执行清理
118+
if loop.is_running():
119+
loop.create_task(close_all_async_resources())
120+
else:
121+
# 否则直接运行直到完成
122+
loop.run_until_complete(close_all_async_resources())
123+
except RuntimeError:
124+
# 如果没有事件循环或者事件循环已关闭,忽略错误
125+
pass

tests/test_async_context.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from unittest.mock import AsyncMock, patch
44
from uuid import uuid4
55

6-
from ._catches import ASYNC_CACHES
6+
from ._catches import ASYNC_CACHES, close_all_async_resources
77

88

99
class AsyncContextTTLTest(IsolatedAsyncioTestCase):
@@ -47,3 +47,20 @@ async def echo(x):
4747
mock_get.assert_called_once()
4848
mock_put.assert_not_called()
4949
self.assertEqual(result, val)
50+
51+
52+
# 模块级别的清理函数,在所有测试运行完毕后调用
53+
def tearDownModule():
54+
"""在模块中的所有测试运行完毕后清理异步资源"""
55+
try:
56+
# 获取当前事件循环
57+
loop = asyncio.get_event_loop()
58+
# 如果事件循环还在运行,则执行清理
59+
if loop.is_running():
60+
loop.create_task(close_all_async_resources())
61+
else:
62+
# 否则直接运行直到完成
63+
loop.run_until_complete(close_all_async_resources())
64+
except RuntimeError:
65+
# 如果没有事件循环或者事件循环已关闭,忽略错误
66+
pass

0 commit comments

Comments
 (0)