Skip to content
2 changes: 2 additions & 0 deletions redis/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from redis import asyncio # noqa
from redis.backoff import default_backoff
from redis.client import Redis, StrictRedis
from redis.driver_info import DriverInfo
from redis.cluster import RedisCluster
from redis.connection import (
BlockingConnectionPool,
Expand Down Expand Up @@ -63,6 +64,7 @@ def int_or_str(value):
"CredentialProvider",
"CrossSlotTransactionError",
"DataError",
"DriverInfo",
"from_url",
"default_backoff",
"InvalidPipelineStack",
Expand Down
21 changes: 16 additions & 5 deletions redis/asyncio/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
list_or_args,
)
from redis.credentials import CredentialProvider
from redis.driver_info import DriverInfo, resolve_driver_info
from redis.event import (
AfterPooledConnectionsInstantiationEvent,
AfterPubSubConnectionInstantiationEvent,
Expand All @@ -74,7 +75,6 @@
_set_info_logger,
deprecated_args,
deprecated_function,
get_lib_version,
safe_str,
str_if_bytes,
truncate_text,
Expand Down Expand Up @@ -214,6 +214,11 @@ def from_pool(
reason="TimeoutError is included by default.",
version="6.0.0",
)
@deprecated_args(
args_to_warn=["lib_name", "lib_version"],
reason="Use 'driver_info' parameter instead. "
"lib_name and lib_version will be removed in a future version.",
)
def __init__(
self,
*,
Expand Down Expand Up @@ -252,8 +257,9 @@ def __init__(
single_connection_client: bool = False,
health_check_interval: int = 0,
client_name: Optional[str] = None,
lib_name: Optional[str] = "redis-py",
lib_version: Optional[str] = get_lib_version(),
lib_name: Optional[str] = None,
lib_version: Optional[str] = None,
driver_info: Optional["DriverInfo"] = None,
username: Optional[str] = None,
auto_close_connection_pool: Optional[bool] = None,
redis_connect_func=None,
Expand Down Expand Up @@ -306,6 +312,12 @@ def __init__(
# Create internal connection pool, expected to be closed by Redis instance
if not retry_on_error:
retry_on_error = []

# Handle driver_info: if provided, use it; otherwise create from lib_name/lib_version
computed_driver_info = resolve_driver_info(
driver_info, lib_name, lib_version
)

kwargs = {
"db": db,
"username": username,
Expand All @@ -320,8 +332,7 @@ def __init__(
"max_connections": max_connections,
"health_check_interval": health_check_interval,
"client_name": client_name,
"lib_name": lib_name,
"lib_version": lib_version,
"driver_info": computed_driver_info,
"redis_connect_func": redis_connect_func,
"protocol": protocol,
}
Expand Down
52 changes: 41 additions & 11 deletions redis/asyncio/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
VerifyFlags = None

from ..auth.token import TokenInterface
from ..driver_info import DriverInfo, resolve_driver_info
from ..event import AsyncAfterConnectionReleasedEvent, EventDispatcher
from ..utils import deprecated_args, format_error_message

Expand All @@ -63,7 +64,7 @@
TimeoutError,
)
from redis.typing import EncodableT
from redis.utils import HIREDIS_AVAILABLE, get_lib_version, str_if_bytes
from redis.utils import HIREDIS_AVAILABLE, str_if_bytes

from .._parsers import (
BaseParser,
Expand Down Expand Up @@ -137,6 +138,11 @@ class AbstractConnection:
"__dict__",
)

@deprecated_args(
args_to_warn=["lib_name", "lib_version"],
reason="Use 'driver_info' parameter instead. "
"lib_name and lib_version will be removed in a future version.",
)
def __init__(
self,
*,
Expand All @@ -153,8 +159,9 @@ def __init__(
socket_read_size: int = 65536,
health_check_interval: float = 0,
client_name: Optional[str] = None,
lib_name: Optional[str] = "redis-py",
lib_version: Optional[str] = get_lib_version(),
lib_name: Optional[str] = None,
lib_version: Optional[str] = None,
driver_info: Optional[DriverInfo] = None,
username: Optional[str] = None,
retry: Optional[Retry] = None,
redis_connect_func: Optional[ConnectCallbackT] = None,
Expand All @@ -163,6 +170,20 @@ def __init__(
protocol: Optional[int] = 2,
event_dispatcher: Optional[EventDispatcher] = None,
):
"""
Initialize a new async Connection.

Parameters
----------
driver_info : DriverInfo, optional
Driver metadata for CLIENT SETINFO. If provided, lib_name and lib_version
are ignored. If not provided, a DriverInfo will be created from lib_name
and lib_version (or defaults if those are also None).
lib_name : str, optional
**Deprecated.** Use driver_info instead. Library name for CLIENT SETINFO.
lib_version : str, optional
**Deprecated.** Use driver_info instead. Library version for CLIENT SETINFO.
"""
if (username or password) and credential_provider is not None:
raise DataError(
"'username' and 'password' cannot be passed along with 'credential_"
Expand All @@ -176,8 +197,10 @@ def __init__(
self._event_dispatcher = event_dispatcher
self.db = db
self.client_name = client_name
self.lib_name = lib_name
self.lib_version = lib_version

# Handle driver_info: if provided, use it; otherwise create from lib_name/lib_version
self.driver_info = resolve_driver_info(driver_info, lib_name, lib_version)

self.credential_provider = credential_provider
self.password = password
self.username = username
Expand Down Expand Up @@ -452,29 +475,36 @@ async def on_connect_check_health(self, check_health: bool = True) -> None:
if str_if_bytes(await self.read_response()) != "OK":
raise ConnectionError("Error setting client name")

# set the library name and version, pipeline for lower startup latency
if self.lib_name:
# Set the library name and version from driver_info, pipeline for lower startup latency
lib_name_sent = False
lib_version_sent = False

if self.driver_info and self.driver_info.formatted_name:
await self.send_command(
"CLIENT",
"SETINFO",
"LIB-NAME",
self.lib_name,
self.driver_info.formatted_name,
check_health=check_health,
)
if self.lib_version:
lib_name_sent = True

if self.driver_info and self.driver_info.lib_version:
await self.send_command(
"CLIENT",
"SETINFO",
"LIB-VER",
self.lib_version,
self.driver_info.lib_version,
check_health=check_health,
)
lib_version_sent = True

# if a database is specified, switch to it. Also pipeline this
if self.db:
await self.send_command("SELECT", self.db, check_health=check_health)

# read responses from pipeline
for _ in (sent for sent in (self.lib_name, self.lib_version) if sent):
for _ in range(sum([lib_name_sent, lib_version_sent])):
try:
await self.read_response()
except ResponseError:
Expand Down
30 changes: 25 additions & 5 deletions redis/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
UnixDomainSocketConnection,
)
from redis.credentials import CredentialProvider
from redis.driver_info import DriverInfo, resolve_driver_info
from redis.event import (
AfterPooledConnectionsInstantiationEvent,
AfterPubSubConnectionInstantiationEvent,
Expand All @@ -63,7 +64,6 @@
from redis.utils import (
_set_info_logger,
deprecated_args,
get_lib_version,
safe_str,
str_if_bytes,
truncate_text,
Expand Down Expand Up @@ -199,6 +199,11 @@ def from_pool(
reason="TimeoutError is included by default.",
version="6.0.0",
)
@deprecated_args(
args_to_warn=["lib_name", "lib_version"],
reason="Use 'driver_info' parameter instead. "
"lib_name and lib_version will be removed in a future version.",
)
def __init__(
self,
host: str = "localhost",
Expand Down Expand Up @@ -240,8 +245,9 @@ def __init__(
single_connection_client: bool = False,
health_check_interval: int = 0,
client_name: Optional[str] = None,
lib_name: Optional[str] = "redis-py",
lib_version: Optional[str] = get_lib_version(),
lib_name: Optional[str] = None,
lib_version: Optional[str] = None,
driver_info: Optional["DriverInfo"] = None,
username: Optional[str] = None,
redis_connect_func: Optional[Callable[[], None]] = None,
credential_provider: Optional[CredentialProvider] = None,
Expand Down Expand Up @@ -280,6 +286,15 @@ def __init__(
decode_responses:
if `True`, the response will be decoded to utf-8.
Argument is ignored when connection_pool is provided.
driver_info:
Optional DriverInfo object to identify upstream libraries.
If provided, lib_name and lib_version are ignored.
If not provided, a DriverInfo will be created from lib_name and lib_version.
Argument is ignored when connection_pool is provided.
lib_name:
**Deprecated.** Use driver_info instead. Library name for CLIENT SETINFO.
lib_version:
**Deprecated.** Use driver_info instead. Library version for CLIENT SETINFO.
maint_notifications_config:
configuration the pool to support maintenance notifications - see
`redis.maint_notifications.MaintNotificationsConfig` for details.
Expand All @@ -296,6 +311,12 @@ def __init__(
if not connection_pool:
if not retry_on_error:
retry_on_error = []

# Handle driver_info: if provided, use it; otherwise create from lib_name/lib_version
computed_driver_info = resolve_driver_info(
driver_info, lib_name, lib_version
)

kwargs = {
"db": db,
"username": username,
Expand All @@ -309,8 +330,7 @@ def __init__(
"max_connections": max_connections,
"health_check_interval": health_check_interval,
"client_name": client_name,
"lib_name": lib_name,
"lib_version": lib_version,
"driver_info": computed_driver_info,
"redis_connect_func": redis_connect_func,
"credential_provider": credential_provider,
"protocol": protocol,
Expand Down
40 changes: 30 additions & 10 deletions redis/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from .auth.token import TokenInterface
from .backoff import NoBackoff
from .credentials import CredentialProvider, UsernamePasswordCredentialProvider
from .driver_info import DriverInfo, resolve_driver_info
from .event import AfterConnectionReleasedEvent, EventDispatcher
from .exceptions import (
AuthenticationError,
Expand Down Expand Up @@ -62,7 +63,6 @@
deprecated_args,
ensure_string,
format_error_message,
get_lib_version,
str_if_bytes,
)

Expand Down Expand Up @@ -655,6 +655,11 @@ def reset_tmp_settings(
class AbstractConnection(MaintNotificationsAbstractConnection, ConnectionInterface):
"Manages communication to and from a Redis server"

@deprecated_args(
args_to_warn=["lib_name", "lib_version"],
reason="Use 'driver_info' parameter instead. "
"lib_name and lib_version will be removed in a future version.",
)
def __init__(
self,
db: int = 0,
Expand All @@ -670,8 +675,9 @@ def __init__(
socket_read_size: int = 65536,
health_check_interval: int = 0,
client_name: Optional[str] = None,
lib_name: Optional[str] = "redis-py",
lib_version: Optional[str] = get_lib_version(),
lib_name: Optional[str] = None,
lib_version: Optional[str] = None,
driver_info: Optional[DriverInfo] = None,
username: Optional[str] = None,
retry: Union[Any, None] = None,
redis_connect_func: Optional[Callable[[], None]] = None,
Expand All @@ -691,10 +697,22 @@ def __init__(
):
"""
Initialize a new Connection.

To specify a retry policy for specific errors, first set
`retry_on_error` to a list of the error/s to retry on, then set
`retry` to a valid `Retry` object.
To retry on TimeoutError, `retry_on_timeout` can also be set to `True`.

Parameters
----------
driver_info : DriverInfo, optional
Driver metadata for CLIENT SETINFO. If provided, lib_name and lib_version
are ignored. If not provided, a DriverInfo will be created from lib_name
and lib_version (or defaults if those are also None).
lib_name : str, optional
**Deprecated.** Use driver_info instead. Library name for CLIENT SETINFO.
lib_version : str, optional
**Deprecated.** Use driver_info instead. Library version for CLIENT SETINFO.
"""
if (username or password) and credential_provider is not None:
raise DataError(
Expand All @@ -710,8 +728,10 @@ def __init__(
self.pid = os.getpid()
self.db = db
self.client_name = client_name
self.lib_name = lib_name
self.lib_version = lib_version

# Handle driver_info: if provided, use it; otherwise create from lib_name/lib_version
self.driver_info = resolve_driver_info(driver_info, lib_name, lib_version)

self.credential_provider = credential_provider
self.password = password
self.username = username
Expand Down Expand Up @@ -988,27 +1008,27 @@ def on_connect_check_health(self, check_health: bool = True):
if str_if_bytes(self.read_response()) != "OK":
raise ConnectionError("Error setting client name")

# Set the library name and version from driver_info
try:
# set the library name and version
if self.lib_name:
if self.driver_info and self.driver_info.formatted_name:
self.send_command(
"CLIENT",
"SETINFO",
"LIB-NAME",
self.lib_name,
self.driver_info.formatted_name,
check_health=check_health,
)
self.read_response()
except ResponseError:
pass

try:
if self.lib_version:
if self.driver_info and self.driver_info.lib_version:
self.send_command(
"CLIENT",
"SETINFO",
"LIB-VER",
self.lib_version,
self.driver_info.lib_version,
check_health=check_health,
)
self.read_response()
Expand Down
Loading
Loading