Skip to content

Commit f7a9b77

Browse files
committed
support report libname and libver to Redis
1 parent e1017fd commit f7a9b77

File tree

9 files changed

+119
-1
lines changed

9 files changed

+119
-1
lines changed

redis/asyncio/client.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import copy
33
import inspect
44
import re
5+
import sys
56
import warnings
67
from typing import (
78
TYPE_CHECKING,
@@ -59,6 +60,11 @@
5960
from redis.typing import ChannelT, EncodableT, KeyT
6061
from redis.utils import safe_str, str_if_bytes
6162

63+
if sys.version_info >= (3, 8):
64+
from importlib import metadata
65+
else:
66+
import importlib_metadata as metadata
67+
6268
PubSubHandler = Callable[[Dict[str, str]], Awaitable[None]]
6369
_KeyT = TypeVar("_KeyT", bound=KeyT)
6470
_ArgT = TypeVar("_ArgT", KeyT, EncodableT)
@@ -67,6 +73,13 @@
6773
if TYPE_CHECKING:
6874
from redis.commands.core import Script
6975

76+
# default libname and libver
77+
try:
78+
LIBVER = metadata.version("redis")
79+
except metadata.PackageNotFoundError:
80+
LIBVER = "99.99.99"
81+
LIBNAME = "redis-py"
82+
7083

7184
class ResponseCallbackProtocol(Protocol):
7285
def __call__(self, response: Any, **kwargs):
@@ -171,6 +184,8 @@ def __init__(
171184
single_connection_client: bool = False,
172185
health_check_interval: int = 0,
173186
client_name: Optional[str] = None,
187+
lib_name: str = LIBNAME,
188+
lib_ver: str = LIBVER,
174189
username: Optional[str] = None,
175190
retry: Optional[Retry] = None,
176191
auto_close_connection_pool: bool = True,
@@ -212,6 +227,8 @@ def __init__(
212227
"max_connections": max_connections,
213228
"health_check_interval": health_check_interval,
214229
"client_name": client_name,
230+
"lib_name": lib_name,
231+
"lib_ver": lib_ver,
215232
"redis_connect_func": redis_connect_func,
216233
}
217234
# based on input, setup appropriate connection args

redis/asyncio/connection.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,8 @@ class Connection:
457457
"db",
458458
"username",
459459
"client_name",
460+
"lib_name",
461+
"lib_ver",
460462
"credential_provider",
461463
"password",
462464
"socket_timeout",
@@ -503,6 +505,8 @@ def __init__(
503505
socket_read_size: int = 65536,
504506
health_check_interval: float = 0,
505507
client_name: Optional[str] = None,
508+
lib_name: Optional[str] = None,
509+
lib_ver: Optional[str] = None,
506510
username: Optional[str] = None,
507511
retry: Optional[Retry] = None,
508512
redis_connect_func: Optional[ConnectCallbackT] = None,
@@ -521,6 +525,8 @@ def __init__(
521525
self.port = int(port)
522526
self.db = db
523527
self.client_name = client_name
528+
self.lib_name = lib_name
529+
self.lib_ver = lib_ver
524530
self.credential_provider = credential_provider
525531
self.password = password
526532
self.username = username
@@ -717,7 +723,18 @@ async def on_connect(self) -> None:
717723
await self.send_command("CLIENT", "SETNAME", self.client_name)
718724
if str_if_bytes(await self.read_response()) != "OK":
719725
raise ConnectionError("Error setting client name")
720-
726+
if self.lib_name:
727+
await self.send_command("CLIENT", "SETINFO", "LIB-NAME", self.lib_name)
728+
try:
729+
await self.read_response()
730+
except:
731+
pass
732+
if self.lib_ver:
733+
await self.send_command("CLIENT", "SETINFO", "LIB-VER", self.lib_ver)
734+
try:
735+
await self.read_response()
736+
except:
737+
pass
721738
# if a database is specified, switch to it
722739
if self.db:
723740
await self.send_command("SELECT", self.db)

redis/client.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import copy
22
import datetime
33
import re
4+
import sys
45
import threading
56
import time
67
import warnings
@@ -29,12 +30,24 @@
2930
from redis.retry import Retry
3031
from redis.utils import safe_str, str_if_bytes
3132

33+
if sys.version_info >= (3, 8):
34+
from importlib import metadata
35+
else:
36+
import importlib_metadata as metadata
37+
3238
SYM_EMPTY = b""
3339
EMPTY_RESPONSE = "EMPTY_RESPONSE"
3440

3541
# some responses (ie. dump) are binary, and just meant to never be decoded
3642
NEVER_DECODE = "NEVER_DECODE"
3743

44+
# default libname and libver
45+
try:
46+
LIBVER = metadata.version("redis")
47+
except metadata.PackageNotFoundError:
48+
LIBVER = "99.99.99"
49+
LIBNAME = "redis-py"
50+
3851

3952
def timestamp_to_datetime(response):
4053
"Converts a unix timestamp to a Python datetime object"
@@ -745,6 +758,7 @@ class AbstractRedis:
745758
"CLIENT LIST": parse_client_list,
746759
"CLIENT INFO": parse_client_info,
747760
"CLIENT SETNAME": bool_ok,
761+
"CLIENT SETINFO": bool_ok,
748762
"CLIENT UNBLOCK": lambda r: r and int(r) == 1 or False,
749763
"CLIENT PAUSE": bool_ok,
750764
"CLIENT GETREDIR": int,
@@ -938,6 +952,8 @@ def __init__(
938952
single_connection_client=False,
939953
health_check_interval=0,
940954
client_name=None,
955+
lib_name=LIBNAME,
956+
lib_ver=LIBVER,
941957
username=None,
942958
retry=None,
943959
redis_connect_func=None,
@@ -988,6 +1004,8 @@ def __init__(
9881004
"max_connections": max_connections,
9891005
"health_check_interval": health_check_interval,
9901006
"client_name": client_name,
1007+
"lib_name": lib_name,
1008+
"lib_ver": lib_ver,
9911009
"redis_connect_func": redis_connect_func,
9921010
"credential_provider": credential_provider,
9931011
}

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: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,8 @@ def __init__(
600600
socket_read_size=65536,
601601
health_check_interval=0,
602602
client_name=None,
603+
lib_name=None,
604+
lib_ver=None,
603605
username=None,
604606
retry=None,
605607
redis_connect_func=None,
@@ -623,6 +625,8 @@ def __init__(
623625
self.pid = os.getpid()
624626
self.db = db
625627
self.client_name = client_name
628+
self.lib_name = lib_name
629+
self.lib_ver = lib_ver
626630
self.credential_provider = credential_provider
627631
self.password = password
628632
self.username = username
@@ -768,6 +772,17 @@ def on_connect(self):
768772
self.send_command("CLIENT", "SETNAME", self.client_name)
769773
if str_if_bytes(self.read_response()) != "OK":
770774
raise ConnectionError("Error setting client name")
775+
if self.lib_name and self.lib_ver:
776+
self.send_command("CLIENT", "SETINFO", "LIB-NAME", self.lib_name)
777+
try:
778+
self.read_response()
779+
except:
780+
pass
781+
self.send_command("CLIENT", "SETINFO", "LIB-VERSION", self.lib_ver)
782+
try:
783+
self.read_response()
784+
except:
785+
pass
771786

772787
# if a database is specified, switch to it
773788
if self.db:

tests/test_asyncio/test_cluster.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,14 @@ 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+
async def test_client_setinfo(self, r: RedisCluster) -> None:
874+
node = r.get_random_node()
875+
await r.client_setinfo("lib-name", "redis-py", target_nodes=node)
876+
await r.client_setinfo("lib-ver", "4.33", target_nodes=node)
877+
info = await r.client_info(target_nodes=node)
878+
assert "lib-name" in info
879+
assert "lib-ver" in info
880+
873881
async def test_exists(self, r: RedisCluster) -> None:
874882
d = {"a": b"1", "b": b"2", "c": b"3", "d": b"4"}
875883
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)