Skip to content

Commit 8804ed2

Browse files
committed
Add a "from_pool" argument for the sync client too
1 parent 308f124 commit 8804ed2

File tree

2 files changed

+108
-5
lines changed

2 files changed

+108
-5
lines changed

redis/client.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ class initializer. In the case of conflicting arguments, querystring
137137
single_connection_client = kwargs.pop("single_connection_client", False)
138138
connection_pool = ConnectionPool.from_url(url, **kwargs)
139139
return cls(
140-
connection_pool=connection_pool,
140+
from_pool=connection_pool,
141141
single_connection_client=single_connection_client,
142142
)
143143

@@ -152,6 +152,7 @@ def __init__(
152152
socket_keepalive=None,
153153
socket_keepalive_options=None,
154154
connection_pool=None,
155+
from_pool=None,
155156
unix_socket_path=None,
156157
encoding="utf-8",
157158
encoding_errors="strict",
@@ -198,7 +199,7 @@ def __init__(
198199
if `True`, connection pool is not used. In that case `Redis`
199200
instance use is not thread safe.
200201
"""
201-
if not connection_pool:
202+
if not connection_pool and not from_pool:
202203
if charset is not None:
203204
warnings.warn(
204205
DeprecationWarning(
@@ -274,8 +275,20 @@ def __init__(
274275
"ssl_ocsp_expected_cert": ssl_ocsp_expected_cert,
275276
}
276277
)
277-
connection_pool = ConnectionPool(**kwargs)
278-
self.connection_pool = connection_pool
278+
from_pool = ConnectionPool(**kwargs)
279+
280+
if from_pool is not None:
281+
# internal connection pool, expected to be closed by Redis instance
282+
if connection_pool is not None:
283+
raise ValueError(
284+
"Cannot use both from_pool and connection_pool arguments")
285+
self.connection_pool = from_pool
286+
self.auto_close_connection_pool = True # the Redis instance closes the pool
287+
else:
288+
# external connection pool, expected to be closed by caller
289+
self.connection_pool = connection_pool
290+
self.auto_close_connection_pool = False # the user is expected to close the pool
291+
279292
self.connection = None
280293
if single_connection_client:
281294
self.connection = self.connection_pool.get_connection("_")
@@ -477,6 +490,9 @@ def close(self):
477490
self.connection = None
478491
self.connection_pool.release(conn)
479492

493+
if self.auto_close_connection_pool:
494+
self.connection_pool.disconnect()
495+
480496
def _send_command_parse_response(self, conn, command_name, *args, **options):
481497
"""
482498
Send a command and parse the response

tests/test_connection.py

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,15 @@
55

66
import pytest
77
import redis
8+
from redis import ConnectionPool, Redis
89
from redis._parsers import _HiredisParser, _RESP2Parser, _RESP3Parser
910
from redis.backoff import NoBackoff
10-
from redis.connection import Connection, SSLConnection, UnixDomainSocketConnection
11+
from redis.connection import (
12+
Connection,
13+
SSLConnection,
14+
UnixDomainSocketConnection,
15+
parse_url,
16+
)
1117
from redis.exceptions import ConnectionError, InvalidResponse, TimeoutError
1218
from redis.retry import Retry
1319
from redis.utils import HIREDIS_AVAILABLE
@@ -209,3 +215,84 @@ def test_create_single_connection_client_from_url():
209215
"redis://localhost:6379/0?", single_connection_client=True
210216
)
211217
assert client.connection is not None
218+
219+
220+
@pytest.mark.parametrize("from_url", (True, False), ids=("from_url", "from_args"))
221+
def test_pool_auto_close(request, from_url):
222+
"""Verify that basic Redis instances have auto_close_connection_pool set to True"""
223+
224+
url: str = request.config.getoption("--redis-url")
225+
url_args = parse_url(url)
226+
227+
def get_redis_connection():
228+
if from_url:
229+
return Redis.from_url(url)
230+
return Redis(**url_args)
231+
232+
r1 = get_redis_connection()
233+
assert r1.auto_close_connection_pool is True
234+
r1.close()
235+
236+
237+
@pytest.mark.parametrize("from_url", (True, False), ids=("from_url", "from_args"))
238+
def test_redis_connection_pool(request, from_url):
239+
"""Verify that basic Redis instances using `connection_pool`
240+
have auto_close_connection_pool set to False"""
241+
242+
url: str = request.config.getoption("--redis-url")
243+
url_args = parse_url(url)
244+
245+
pool = None
246+
247+
def get_redis_connection():
248+
nonlocal pool
249+
if from_url:
250+
pool = ConnectionPool.from_url(url)
251+
else:
252+
pool = ConnectionPool(**url_args)
253+
return Redis(connection_pool=pool)
254+
255+
called = 0
256+
257+
def mock_disconnect(_):
258+
nonlocal called
259+
called += 1
260+
261+
with patch.object(ConnectionPool, "disconnect", mock_disconnect):
262+
with get_redis_connection() as r1:
263+
assert r1.auto_close_connection_pool is False
264+
265+
assert called == 0
266+
pool.disconnect()
267+
268+
269+
@pytest.mark.parametrize("from_url", (True, False), ids=("from_url", "from_args"))
270+
def test_redis_from_pool(request, from_url):
271+
"""Verify that basic Redis instances using `from_pool`
272+
have auto_close_connection_pool set to True"""
273+
274+
url: str = request.config.getoption("--redis-url")
275+
url_args = parse_url(url)
276+
277+
pool = None
278+
279+
def get_redis_connection():
280+
nonlocal pool
281+
if from_url:
282+
pool = ConnectionPool.from_url(url)
283+
else:
284+
pool = ConnectionPool(**url_args)
285+
return Redis(from_pool=pool)
286+
287+
called = 0
288+
289+
def mock_disconnect(_):
290+
nonlocal called
291+
called += 1
292+
293+
with patch.object(ConnectionPool, "disconnect", mock_disconnect):
294+
with get_redis_connection() as r1:
295+
assert r1.auto_close_connection_pool is True
296+
297+
assert called == 1
298+
pool.disconnect()

0 commit comments

Comments
 (0)