Skip to content

Commit 4ebfde6

Browse files
committed
support report libname and libver to Redis
1 parent 7fc4c76 commit 4ebfde6

File tree

10 files changed

+100
-2
lines changed

10 files changed

+100
-2
lines changed

redis/asyncio/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ def __init__(
171171
single_connection_client: bool = False,
172172
health_check_interval: int = 0,
173173
client_name: Optional[str] = None,
174+
set_lib: bool = True,
174175
username: Optional[str] = None,
175176
retry: Optional[Retry] = None,
176177
auto_close_connection_pool: bool = True,
@@ -212,6 +213,7 @@ def __init__(
212213
"max_connections": max_connections,
213214
"health_check_interval": health_check_interval,
214215
"client_name": client_name,
216+
"set_lib": set_lib,
215217
"redis_connect_func": redis_connect_func,
216218
}
217219
# based on input, setup appropriate connection args

redis/asyncio/connection.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
TimeoutError,
5656
)
5757
from redis.typing import EncodableT, EncodedT
58-
from redis.utils import HIREDIS_AVAILABLE, str_if_bytes
58+
from redis.utils import HIREDIS_AVAILABLE, get_lib, str_if_bytes
5959

6060
hiredis = None
6161
if HIREDIS_AVAILABLE:
@@ -458,6 +458,7 @@ class Connection:
458458
"db",
459459
"username",
460460
"client_name",
461+
"set_lib",
461462
"credential_provider",
462463
"password",
463464
"socket_timeout",
@@ -504,6 +505,7 @@ def __init__(
504505
socket_read_size: int = 65536,
505506
health_check_interval: float = 0,
506507
client_name: Optional[str] = None,
508+
set_lib: bool = False,
507509
username: Optional[str] = None,
508510
retry: Optional[Retry] = None,
509511
redis_connect_func: Optional[ConnectCallbackT] = None,
@@ -522,6 +524,7 @@ def __init__(
522524
self.port = int(port)
523525
self.db = db
524526
self.client_name = client_name
527+
self.set_lib = set_lib
525528
self.credential_provider = credential_provider
526529
self.password = password
527530
self.username = username
@@ -718,7 +721,14 @@ async def on_connect(self) -> None:
718721
await self.send_command("CLIENT", "SETNAME", self.client_name)
719722
if str_if_bytes(await self.read_response()) != "OK":
720723
raise ConnectionError("Error setting client name")
721-
724+
if self.set_lib is True and self.is_connected is False:
725+
try:
726+
await self.send_command("CLIENT", "SETINFO", "LIB-NAME", "redis-py")
727+
await self.read_response()
728+
await self.send_command("CLIENT", "SETINFO", "LIB-VERSION", get_lib())
729+
await self.read_response()
730+
except Exception:
731+
pass
722732
# if a database is specified, switch to it
723733
if self.db:
724734
await self.send_command("SELECT", self.db)

redis/client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,7 @@ class AbstractRedis:
745745
"CLIENT LIST": parse_client_list,
746746
"CLIENT INFO": parse_client_info,
747747
"CLIENT SETNAME": bool_ok,
748+
"CLIENT SETINFO": bool_ok,
748749
"CLIENT UNBLOCK": lambda r: r and int(r) == 1 or False,
749750
"CLIENT PAUSE": bool_ok,
750751
"CLIENT GETREDIR": int,
@@ -938,6 +939,7 @@ def __init__(
938939
single_connection_client=False,
939940
health_check_interval=0,
940941
client_name=None,
942+
set_lib=True,
941943
username=None,
942944
retry=None,
943945
redis_connect_func=None,
@@ -988,6 +990,7 @@ def __init__(
988990
"max_connections": max_connections,
989991
"health_check_interval": health_check_interval,
990992
"client_name": client_name,
993+
"set_lib": set_lib,
991994
"redis_connect_func": redis_connect_func,
992995
"credential_provider": credential_provider,
993996
}

redis/cluster.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ class AbstractRedisCluster:
212212
"AUTH",
213213
"CLIENT LIST",
214214
"CLIENT SETNAME",
215+
"CLIENT SETINFO",
215216
"CLIENT GETNAME",
216217
"CONFIG SET",
217218
"CONFIG REWRITE",

redis/commands/core.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,14 @@ def client_setname(self, name: str, **kwargs) -> ResponseT:
706706
"""
707707
return self.execute_command("CLIENT SETNAME", name, **kwargs)
708708

709+
def client_setinfo(self, attr: str, value: str, **kwargs) -> ResponseT:
710+
"""
711+
Sets the current connection libname
712+
713+
For mor information see https://redis.io/commands/client-setinfo
714+
"""
715+
return self.execute_command("CLIENT SETINFO", attr, value, **kwargs)
716+
709717
def client_unblock(
710718
self, client_id: int, error: bool = False, **kwargs
711719
) -> ResponseT:

redis/connection.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
CRYPTOGRAPHY_AVAILABLE,
3939
HIREDIS_AVAILABLE,
4040
HIREDIS_PACK_AVAILABLE,
41+
get_lib,
4142
str_if_bytes,
4243
)
4344

@@ -601,6 +602,7 @@ def __init__(
601602
socket_read_size=65536,
602603
health_check_interval=0,
603604
client_name=None,
605+
set_lib=False,
604606
username=None,
605607
retry=None,
606608
redis_connect_func=None,
@@ -624,6 +626,7 @@ def __init__(
624626
self.pid = os.getpid()
625627
self.db = db
626628
self.client_name = client_name
629+
self.set_lib = set_lib
627630
self.credential_provider = credential_provider
628631
self.password = password
629632
self.username = username
@@ -739,6 +742,10 @@ def _error_message(self, exception):
739742
def on_connect(self):
740743
"Initialize the connection, authenticate and select a database"
741744
self._parser.on_connect(self)
745+
if ssl_available:
746+
authened = False
747+
else:
748+
authened = True
742749

743750
# if credential provider or username and/or password are set, authenticate
744751
if self.credential_provider or (self.username or self.password):
@@ -763,12 +770,22 @@ def on_connect(self):
763770

764771
if str_if_bytes(auth_response) != "OK":
765772
raise AuthenticationError("Invalid Username or Password")
773+
else:
774+
authened = True
766775

767776
# if a client_name is given, set it
768777
if self.client_name:
769778
self.send_command("CLIENT", "SETNAME", self.client_name)
770779
if str_if_bytes(self.read_response()) != "OK":
771780
raise ConnectionError("Error setting client name")
781+
if self.set_lib is True and authened is True:
782+
try:
783+
self.send_command("CLIENT", "SETINFO", "LIB-NAME", "redis-py")
784+
self.read_response()
785+
self.send_command("CLIENT", "SETINFO", "LIB-VERSION", get_lib())
786+
self.read_response()
787+
except Exception:
788+
pass
772789

773790
# if a database is specified, switch to it
774791
if self.db:

redis/utils.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import sys
12
from contextlib import contextmanager
23
from functools import wraps
34
from typing import Any, Dict, Mapping, Union
@@ -19,6 +20,11 @@
1920
except ImportError:
2021
CRYPTOGRAPHY_AVAILABLE = False
2122

23+
if sys.version_info >= (3, 8):
24+
from importlib import metadata
25+
else:
26+
import importlib_metadata as metadata
27+
2228

2329
def from_url(url, **kwargs):
2430
"""
@@ -110,3 +116,11 @@ def wrapper(*args, **kwargs):
110116
return wrapper
111117

112118
return decorator
119+
120+
121+
def get_lib():
122+
try:
123+
libver = metadata.version("redis")
124+
except metadata.PackageNotFoundError:
125+
libver = "99.99.99"
126+
return libver

tests/test_asyncio/test_cluster.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,15 @@ async def test_client_setname(self, r: RedisCluster) -> None:
870870
client_name = await r.client_getname(target_nodes=node)
871871
assert client_name == "redis_py_test"
872872

873+
@skip_if_server_version_lt("7.2.0")
874+
async def test_client_setinfo(self, r: RedisCluster) -> None:
875+
node = r.get_random_node()
876+
await r.client_setinfo("lib-name", "redis-py", target_nodes=node)
877+
await r.client_setinfo("lib-ver", "4.33", target_nodes=node)
878+
info = await r.client_info(target_nodes=node)
879+
assert "lib-name" in info
880+
assert "lib-ver" in info
881+
873882
async def test_exists(self, r: RedisCluster) -> None:
874883
d = {"a": b"1", "b": b"2", "c": b"3", "d": b"4"}
875884
await r.mset_nonatomic(d)

tests/test_asyncio/test_commands.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,15 @@ async def test_client_setname(self, r: redis.Redis):
340340
assert await r.client_setname("redis_py_test")
341341
assert await r.client_getname() == "redis_py_test"
342342

343+
@skip_if_server_version_lt("7.2.0")
344+
@pytest.mark.onlynoncluster
345+
async def test_client_setinfo(self, r: redis.Redis):
346+
assert await r.client_setinfo("lib-name", "redis-py")
347+
assert await r.client_setinfo("lib-ver", "4.33")
348+
info = await r.client_info()
349+
assert "lib-name" in info
350+
assert "lib-ver" in info
351+
343352
@skip_if_server_version_lt("2.6.9")
344353
@pytest.mark.onlynoncluster
345354
async def test_client_kill(self, r: redis.Redis, r2):
@@ -438,6 +447,14 @@ async def test_client_list_after_client_setname(self, r: redis.Redis):
438447
# we don't know which client ours will be
439448
assert "redis_py_test" in [c["name"] for c in clients]
440449

450+
@skip_if_server_version_lt("7.2.0")
451+
async def test_client_list_after_client_setinfo(self, r: redis.Redis):
452+
await r.client_setinfo("lib-name", "redis-py")
453+
await r.client_setinfo("lib-ver", "4.33")
454+
clients = await r.client_list()
455+
assert "redis-py" in [c["lib-name"] for c in clients]
456+
assert "4.33" in [c["lib-ver"] for c in clients]
457+
441458
@skip_if_server_version_lt("2.9.50")
442459
@pytest.mark.onlynoncluster
443460
async def test_client_pause(self, r: redis.Redis):

tests/test_commands.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,15 @@ def test_client_setname(self, r):
530530
assert r.client_setname("redis_py_test")
531531
assert r.client_getname() == "redis_py_test"
532532

533+
@pytest.mark.onlynoncluster
534+
@skip_if_server_version_lt("7.2.0")
535+
def test_client_setinfo(self, r):
536+
assert r.client_setinfo("lib-name", "redis-py")
537+
assert r.client_setinfo("lib-ver", "4.33")
538+
info = r.client_info()
539+
assert "lib-name" in info
540+
assert "lib-ver" in info
541+
533542
@pytest.mark.onlynoncluster
534543
@skip_if_server_version_lt("2.6.9")
535544
def test_client_kill(self, r, r2):
@@ -628,6 +637,14 @@ def test_client_list_after_client_setname(self, r):
628637
# we don't know which client ours will be
629638
assert "redis_py_test" in [c["name"] for c in clients]
630639

640+
@skip_if_server_version_lt("7.2.0")
641+
def test_client_list_after_client_setinfo(self, r):
642+
r.client_setinfo("lib-name", "redis-py")
643+
r.client_setinfo("lib-ver", "4.33")
644+
clients = r.client_list()
645+
assert "redis-py" in [c["lib-name"] for c in clients]
646+
assert "4.33" in [c["lib-ver"] for c in clients]
647+
631648
@skip_if_server_version_lt("6.2.0")
632649
def test_client_kill_filter_by_laddr(self, r, r2):
633650
r.client_setname("redis-py-c1")

0 commit comments

Comments
 (0)