Skip to content

Commit 2300272

Browse files
authored
Merge branch 'master' into temp-disable-aa
2 parents 183fed7 + d411a47 commit 2300272

File tree

13 files changed

+720
-56
lines changed

13 files changed

+720
-56
lines changed

redis/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from redis import asyncio # noqa
22
from redis.backoff import default_backoff
33
from redis.client import Redis, StrictRedis
4+
from redis.driver_info import DriverInfo
45
from redis.cluster import RedisCluster
56
from redis.connection import (
67
BlockingConnectionPool,
@@ -63,6 +64,7 @@ def int_or_str(value):
6364
"CredentialProvider",
6465
"CrossSlotTransactionError",
6566
"DataError",
67+
"DriverInfo",
6668
"from_url",
6769
"default_backoff",
6870
"InvalidPipelineStack",

redis/asyncio/client.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
list_or_args,
5454
)
5555
from redis.credentials import CredentialProvider
56+
from redis.driver_info import DriverInfo, resolve_driver_info
5657
from redis.event import (
5758
AfterPooledConnectionsInstantiationEvent,
5859
AfterPubSubConnectionInstantiationEvent,
@@ -74,7 +75,6 @@
7475
_set_info_logger,
7576
deprecated_args,
7677
deprecated_function,
77-
get_lib_version,
7878
safe_str,
7979
str_if_bytes,
8080
truncate_text,
@@ -214,6 +214,11 @@ def from_pool(
214214
reason="TimeoutError is included by default.",
215215
version="6.0.0",
216216
)
217+
@deprecated_args(
218+
args_to_warn=["lib_name", "lib_version"],
219+
reason="Use 'driver_info' parameter instead. "
220+
"lib_name and lib_version will be removed in a future version.",
221+
)
217222
def __init__(
218223
self,
219224
*,
@@ -252,8 +257,9 @@ def __init__(
252257
single_connection_client: bool = False,
253258
health_check_interval: int = 0,
254259
client_name: Optional[str] = None,
255-
lib_name: Optional[str] = "redis-py",
256-
lib_version: Optional[str] = get_lib_version(),
260+
lib_name: Optional[str] = None,
261+
lib_version: Optional[str] = None,
262+
driver_info: Optional["DriverInfo"] = None,
257263
username: Optional[str] = None,
258264
auto_close_connection_pool: Optional[bool] = None,
259265
redis_connect_func=None,
@@ -306,6 +312,12 @@ def __init__(
306312
# Create internal connection pool, expected to be closed by Redis instance
307313
if not retry_on_error:
308314
retry_on_error = []
315+
316+
# Handle driver_info: if provided, use it; otherwise create from lib_name/lib_version
317+
computed_driver_info = resolve_driver_info(
318+
driver_info, lib_name, lib_version
319+
)
320+
309321
kwargs = {
310322
"db": db,
311323
"username": username,
@@ -320,8 +332,7 @@ def __init__(
320332
"max_connections": max_connections,
321333
"health_check_interval": health_check_interval,
322334
"client_name": client_name,
323-
"lib_name": lib_name,
324-
"lib_version": lib_version,
335+
"driver_info": computed_driver_info,
325336
"redis_connect_func": redis_connect_func,
326337
"protocol": protocol,
327338
}

redis/asyncio/connection.py

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
VerifyFlags = None
3939

4040
from ..auth.token import TokenInterface
41+
from ..driver_info import DriverInfo, resolve_driver_info
4142
from ..event import AsyncAfterConnectionReleasedEvent, EventDispatcher
4243
from ..utils import deprecated_args, format_error_message
4344

@@ -63,7 +64,7 @@
6364
TimeoutError,
6465
)
6566
from redis.typing import EncodableT
66-
from redis.utils import HIREDIS_AVAILABLE, get_lib_version, str_if_bytes
67+
from redis.utils import HIREDIS_AVAILABLE, str_if_bytes
6768

6869
from .._parsers import (
6970
BaseParser,
@@ -137,6 +138,11 @@ class AbstractConnection:
137138
"__dict__",
138139
)
139140

141+
@deprecated_args(
142+
args_to_warn=["lib_name", "lib_version"],
143+
reason="Use 'driver_info' parameter instead. "
144+
"lib_name and lib_version will be removed in a future version.",
145+
)
140146
def __init__(
141147
self,
142148
*,
@@ -153,8 +159,9 @@ def __init__(
153159
socket_read_size: int = 65536,
154160
health_check_interval: float = 0,
155161
client_name: Optional[str] = None,
156-
lib_name: Optional[str] = "redis-py",
157-
lib_version: Optional[str] = get_lib_version(),
162+
lib_name: Optional[str] = None,
163+
lib_version: Optional[str] = None,
164+
driver_info: Optional[DriverInfo] = None,
158165
username: Optional[str] = None,
159166
retry: Optional[Retry] = None,
160167
redis_connect_func: Optional[ConnectCallbackT] = None,
@@ -163,6 +170,20 @@ def __init__(
163170
protocol: Optional[int] = 2,
164171
event_dispatcher: Optional[EventDispatcher] = None,
165172
):
173+
"""
174+
Initialize a new async Connection.
175+
176+
Parameters
177+
----------
178+
driver_info : DriverInfo, optional
179+
Driver metadata for CLIENT SETINFO. If provided, lib_name and lib_version
180+
are ignored. If not provided, a DriverInfo will be created from lib_name
181+
and lib_version (or defaults if those are also None).
182+
lib_name : str, optional
183+
**Deprecated.** Use driver_info instead. Library name for CLIENT SETINFO.
184+
lib_version : str, optional
185+
**Deprecated.** Use driver_info instead. Library version for CLIENT SETINFO.
186+
"""
166187
if (username or password) and credential_provider is not None:
167188
raise DataError(
168189
"'username' and 'password' cannot be passed along with 'credential_"
@@ -176,8 +197,10 @@ def __init__(
176197
self._event_dispatcher = event_dispatcher
177198
self.db = db
178199
self.client_name = client_name
179-
self.lib_name = lib_name
180-
self.lib_version = lib_version
200+
201+
# Handle driver_info: if provided, use it; otherwise create from lib_name/lib_version
202+
self.driver_info = resolve_driver_info(driver_info, lib_name, lib_version)
203+
181204
self.credential_provider = credential_provider
182205
self.password = password
183206
self.username = username
@@ -452,29 +475,36 @@ async def on_connect_check_health(self, check_health: bool = True) -> None:
452475
if str_if_bytes(await self.read_response()) != "OK":
453476
raise ConnectionError("Error setting client name")
454477

455-
# set the library name and version, pipeline for lower startup latency
456-
if self.lib_name:
478+
# Set the library name and version from driver_info, pipeline for lower startup latency
479+
lib_name_sent = False
480+
lib_version_sent = False
481+
482+
if self.driver_info and self.driver_info.formatted_name:
457483
await self.send_command(
458484
"CLIENT",
459485
"SETINFO",
460486
"LIB-NAME",
461-
self.lib_name,
487+
self.driver_info.formatted_name,
462488
check_health=check_health,
463489
)
464-
if self.lib_version:
490+
lib_name_sent = True
491+
492+
if self.driver_info and self.driver_info.lib_version:
465493
await self.send_command(
466494
"CLIENT",
467495
"SETINFO",
468496
"LIB-VER",
469-
self.lib_version,
497+
self.driver_info.lib_version,
470498
check_health=check_health,
471499
)
500+
lib_version_sent = True
501+
472502
# if a database is specified, switch to it. Also pipeline this
473503
if self.db:
474504
await self.send_command("SELECT", self.db, check_health=check_health)
475505

476506
# read responses from pipeline
477-
for _ in (sent for sent in (self.lib_name, self.lib_version) if sent):
507+
for _ in range(sum([lib_name_sent, lib_version_sent])):
478508
try:
479509
await self.read_response()
480510
except ResponseError:
@@ -1215,16 +1245,17 @@ def can_get_connection(self) -> bool:
12151245
version="5.3.0",
12161246
)
12171247
async def get_connection(self, command_name=None, *keys, **options):
1248+
"""Get a connected connection from the pool"""
12181249
async with self._lock:
1219-
"""Get a connected connection from the pool"""
12201250
connection = self.get_available_connection()
1221-
try:
1222-
await self.ensure_connection(connection)
1223-
except BaseException:
1224-
await self.release(connection)
1225-
raise
12261251

1227-
return connection
1252+
# We now perform the connection check outside of the lock.
1253+
try:
1254+
await self.ensure_connection(connection)
1255+
return connection
1256+
except BaseException:
1257+
await self.release(connection)
1258+
raise
12281259

12291260
def get_available_connection(self):
12301261
"""Get a connection from the pool, without making sure it is connected"""

redis/client.py

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
UnixDomainSocketConnection,
4141
)
4242
from redis.credentials import CredentialProvider
43+
from redis.driver_info import DriverInfo, resolve_driver_info
4344
from redis.event import (
4445
AfterPooledConnectionsInstantiationEvent,
4546
AfterPubSubConnectionInstantiationEvent,
@@ -63,7 +64,6 @@
6364
from redis.utils import (
6465
_set_info_logger,
6566
deprecated_args,
66-
get_lib_version,
6767
safe_str,
6868
str_if_bytes,
6969
truncate_text,
@@ -199,6 +199,11 @@ def from_pool(
199199
reason="TimeoutError is included by default.",
200200
version="6.0.0",
201201
)
202+
@deprecated_args(
203+
args_to_warn=["lib_name", "lib_version"],
204+
reason="Use 'driver_info' parameter instead. "
205+
"lib_name and lib_version will be removed in a future version.",
206+
)
202207
def __init__(
203208
self,
204209
host: str = "localhost",
@@ -240,8 +245,9 @@ def __init__(
240245
single_connection_client: bool = False,
241246
health_check_interval: int = 0,
242247
client_name: Optional[str] = None,
243-
lib_name: Optional[str] = "redis-py",
244-
lib_version: Optional[str] = get_lib_version(),
248+
lib_name: Optional[str] = None,
249+
lib_version: Optional[str] = None,
250+
driver_info: Optional["DriverInfo"] = None,
245251
username: Optional[str] = None,
246252
redis_connect_func: Optional[Callable[[], None]] = None,
247253
credential_provider: Optional[CredentialProvider] = None,
@@ -280,6 +286,15 @@ def __init__(
280286
decode_responses:
281287
if `True`, the response will be decoded to utf-8.
282288
Argument is ignored when connection_pool is provided.
289+
driver_info:
290+
Optional DriverInfo object to identify upstream libraries.
291+
If provided, lib_name and lib_version are ignored.
292+
If not provided, a DriverInfo will be created from lib_name and lib_version.
293+
Argument is ignored when connection_pool is provided.
294+
lib_name:
295+
**Deprecated.** Use driver_info instead. Library name for CLIENT SETINFO.
296+
lib_version:
297+
**Deprecated.** Use driver_info instead. Library version for CLIENT SETINFO.
283298
maint_notifications_config:
284299
configuration the pool to support maintenance notifications - see
285300
`redis.maint_notifications.MaintNotificationsConfig` for details.
@@ -296,6 +311,12 @@ def __init__(
296311
if not connection_pool:
297312
if not retry_on_error:
298313
retry_on_error = []
314+
315+
# Handle driver_info: if provided, use it; otherwise create from lib_name/lib_version
316+
computed_driver_info = resolve_driver_info(
317+
driver_info, lib_name, lib_version
318+
)
319+
299320
kwargs = {
300321
"db": db,
301322
"username": username,
@@ -309,8 +330,7 @@ def __init__(
309330
"max_connections": max_connections,
310331
"health_check_interval": health_check_interval,
311332
"client_name": client_name,
312-
"lib_name": lib_name,
313-
"lib_version": lib_version,
333+
"driver_info": computed_driver_info,
314334
"redis_connect_func": redis_connect_func,
315335
"credential_provider": credential_provider,
316336
"protocol": protocol,
@@ -1009,10 +1029,22 @@ def is_health_check_response(self, response) -> bool:
10091029
If there are no subscriptions redis responds to PING command with a
10101030
bulk response, instead of a multi-bulk with "pong" and the response.
10111031
"""
1012-
return response in [
1013-
self.health_check_response, # If there was a subscription
1014-
self.health_check_response_b, # If there wasn't
1015-
]
1032+
if self.encoder.decode_responses:
1033+
return (
1034+
response
1035+
in [
1036+
self.health_check_response, # If there is a subscription
1037+
self.HEALTH_CHECK_MESSAGE, # If there are no subscriptions and decode_responses=True
1038+
]
1039+
)
1040+
else:
1041+
return (
1042+
response
1043+
in [
1044+
self.health_check_response, # If there is a subscription
1045+
self.health_check_response_b, # If there isn't a subscription and decode_responses=False
1046+
]
1047+
)
10161048

10171049
def check_health(self) -> None:
10181050
conn = self.connection

redis/cluster.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2208,7 +2208,8 @@ def _sharded_message_generator(self):
22082208

22092209
def _pubsubs_generator(self):
22102210
while True:
2211-
yield from self.node_pubsub_mapping.values()
2211+
current_nodes = list(self.node_pubsub_mapping.values())
2212+
yield from current_nodes
22122213

22132214
def get_sharded_message(
22142215
self, ignore_subscribe_messages=False, timeout=0.0, target_node=None

0 commit comments

Comments
 (0)