Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
25 changes: 21 additions & 4 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
from redis.event import (
AfterPooledConnectionsInstantiationEvent,
AfterPubSubConnectionInstantiationEvent,
Expand Down Expand Up @@ -214,6 +215,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 @@ -250,8 +256,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 @@ -304,6 +311,17 @@ 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
if driver_info is not None:
computed_driver_info = driver_info
else:
# Fallback: create DriverInfo from lib_name and lib_version
# Use defaults if not provided
name = lib_name if lib_name is not None else "redis-py"
version = lib_version if lib_version is not None else get_lib_version()
computed_driver_info = DriverInfo(name=name, lib_version=version)

kwargs = {
"db": db,
"username": username,
Expand All @@ -318,8 +336,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
60 changes: 50 additions & 10 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
from ..event import AsyncAfterConnectionReleasedEvent, EventDispatcher
from ..utils import deprecated_args, format_error_message

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,17 @@ 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
if driver_info is not None:
self.driver_info = driver_info
else:
# Fallback: create DriverInfo from lib_name and lib_version
# Use defaults if not provided
name = lib_name if lib_name is not None else "redis-py"
version = lib_version if lib_version is not None else get_lib_version()
self.driver_info = DriverInfo(name=name, lib_version=version)

self.credential_provider = credential_provider
self.password = password
self.username = username
Expand Down Expand Up @@ -452,29 +482,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 Expand Up @@ -1174,6 +1211,9 @@ def __init__(
if self._event_dispatcher is None:
self._event_dispatcher = EventDispatcher()

# Store driver_info for propagation to connections
self.driver_info = self.connection_kwargs.get("driver_info", None)

def __repr__(self):
conn_kwargs = ",".join([f"{k}={v}" for k, v in self.connection_kwargs.items()])
return (
Expand Down
34 changes: 30 additions & 4 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
from redis.event import (
AfterPooledConnectionsInstantiationEvent,
AfterPubSubConnectionInstantiationEvent,
Expand Down Expand Up @@ -199,6 +200,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 +246,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 +287,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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current implementation causes to have lib_name to be ignored - it will be valuable addition to the details in the docstrings.

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 +312,17 @@ 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
if driver_info is not None:
computed_driver_info = driver_info
else:
# Fallback: create DriverInfo from lib_name and lib_version
# Use defaults if not provided
name = lib_name if lib_name is not None else "redis-py"
version = lib_version if lib_version is not None else get_lib_version()
computed_driver_info = DriverInfo(name=name, lib_version=version)

kwargs = {
"db": db,
"username": username,
Expand All @@ -309,8 +336,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
49 changes: 40 additions & 9 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
from .event import AfterConnectionReleasedEvent, EventDispatcher
from .exceptions import (
AuthenticationError,
Expand Down Expand Up @@ -655,6 +656,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 +676,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 +698,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 +729,17 @@ 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
if driver_info is not None:
self.driver_info = driver_info
else:
# Fallback: create DriverInfo from lib_name and lib_version
# Use defaults if not provided
name = lib_name if lib_name is not None else "redis-py"
version = lib_version if lib_version is not None else get_lib_version()
self.driver_info = DriverInfo(name=name, lib_version=version)

self.credential_provider = credential_provider
self.password = password
self.username = username
Expand Down Expand Up @@ -988,27 +1016,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 Expand Up @@ -2482,6 +2510,9 @@ def __init__(
if self._event_dispatcher is None:
self._event_dispatcher = EventDispatcher()

# Store driver_info for propagation to connections
self.driver_info = self._connection_kwargs.get("driver_info", None)

# a lock to protect the critical section in _checkpid().
# this lock is acquired when the process id changes, such as
# after a fork. during this time, multiple threads in the child
Expand Down
Loading
Loading