Skip to content

Commit 9681b81

Browse files
authored
Merge branch 'master' into cmd-page-examples
2 parents a38fb80 + b4fcbaa commit 9681b81

File tree

8 files changed

+126
-147
lines changed

8 files changed

+126
-147
lines changed

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
* Prevent async ClusterPipeline instances from becoming "false-y" in case of empty command stack (#3061)
6767
* Close Unix sockets if the connection attempt fails. This prevents `ResourceWarning`s. (#3314)
6868
* Close SSL sockets if the connection attempt fails, or if validations fail. (#3317)
69+
* Eliminate mutable default arguments in the `redis.commands.core.Script` class. (#3332)
6970

7071
* 4.1.3 (Feb 8, 2022)
7172
* Fix flushdb and flushall (#1926)

dev_requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ invoke==2.2.0
88
mock
99
packaging>=20.4
1010
pytest
11-
pytest-asyncio
11+
pytest-asyncio>=0.23.0,<0.24.0
1212
pytest-cov
1313
pytest-profiling
1414
pytest-timeout

pytest.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@ timeout = 30
1414
filterwarnings =
1515
always
1616
ignore:RedisGraph support is deprecated as of Redis Stack 7.2:DeprecationWarning
17+
# Ignore a coverage warning when COVERAGE_CORE=sysmon for Pythons < 3.12.
18+
ignore:sys.monitoring isn't available:coverage.exceptions.CoverageWarning

redis/_parsers/helpers.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,9 +445,11 @@ def parse_cluster_info(response, **options):
445445
def _parse_node_line(line):
446446
line_items = line.split(" ")
447447
node_id, addr, flags, master_id, ping, pong, epoch, connected = line.split(" ")[:8]
448-
addr = addr.split("@")[0]
448+
ip = addr.split("@")[0]
449+
hostname = addr.split("@")[1].split(",")[1] if "@" in addr and "," in addr else ""
449450
node_dict = {
450451
"node_id": node_id,
452+
"hostname": hostname,
451453
"flags": flags,
452454
"master_id": master_id,
453455
"last_ping_sent": ping,
@@ -460,7 +462,7 @@ def _parse_node_line(line):
460462
if len(line_items) >= 9:
461463
slots, migrations = _parse_slots(line_items[8:])
462464
node_dict["slots"], node_dict["migrations"] = slots, migrations
463-
return addr, node_dict
465+
return ip, node_dict
464466

465467

466468
def _parse_slots(slot_ranges):

redis/asyncio/client.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -579,10 +579,12 @@ async def aclose(self, close_connection_pool: Optional[bool] = None) -> None:
579579
"""
580580
Closes Redis client connection
581581
582-
:param close_connection_pool: decides whether to close the connection pool used
583-
by this Redis client, overriding Redis.auto_close_connection_pool. By default,
584-
let Redis.auto_close_connection_pool decide whether to close the connection
585-
pool.
582+
Args:
583+
close_connection_pool:
584+
decides whether to close the connection pool used by this Redis client,
585+
overriding Redis.auto_close_connection_pool.
586+
By default, let Redis.auto_close_connection_pool decide
587+
whether to close the connection pool.
586588
"""
587589
conn = self.connection
588590
if conn:

redis/commands/core.py

Lines changed: 55 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@
4646
from .helpers import list_or_args
4747

4848
if TYPE_CHECKING:
49-
from redis.asyncio.client import Redis as AsyncRedis
50-
from redis.client import Redis
49+
import redis.asyncio.client
50+
import redis.client
5151

5252

5353
class ACLCommands(CommandsProtocol):
@@ -731,16 +731,19 @@ def client_pause(self, timeout: int, all: bool = True, **kwargs) -> ResponseT:
731731
732732
For more information see https://redis.io/commands/client-pause
733733
734-
:param timeout: milliseconds to pause clients
735-
:param all: If true (default) all client commands are blocked.
736-
otherwise, clients are only blocked if they attempt to execute
737-
a write command.
734+
Args:
735+
timeout: milliseconds to pause clients
736+
all: If true (default) all client commands are blocked.
737+
otherwise, clients are only blocked if they attempt to execute
738+
a write command.
739+
738740
For the WRITE mode, some commands have special behavior:
739-
EVAL/EVALSHA: Will block client for all scripts.
740-
PUBLISH: Will block client.
741-
PFCOUNT: Will block client.
742-
WAIT: Acknowledgments will be delayed, so this command will
743-
appear blocked.
741+
742+
* EVAL/EVALSHA: Will block client for all scripts.
743+
* PUBLISH: Will block client.
744+
* PFCOUNT: Will block client.
745+
* WAIT: Acknowledgments will be delayed, so this command will
746+
appear blocked.
744747
"""
745748
args = ["CLIENT PAUSE", str(timeout)]
746749
if not isinstance(timeout, int):
@@ -1439,7 +1442,7 @@ class BitFieldOperation:
14391442

14401443
def __init__(
14411444
self,
1442-
client: Union["Redis", "AsyncRedis"],
1445+
client: Union["redis.client.Redis", "redis.asyncio.client.Redis"],
14431446
key: str,
14441447
default_overflow: Union[str, None] = None,
14451448
):
@@ -1583,7 +1586,7 @@ def bitcount(
15831586
return self.execute_command("BITCOUNT", *params, keys=[key])
15841587

15851588
def bitfield(
1586-
self: Union["Redis", "AsyncRedis"],
1589+
self: Union["redis.client.Redis", "redis.asyncio.client.Redis"],
15871590
key: KeyT,
15881591
default_overflow: Union[str, None] = None,
15891592
) -> BitFieldOperation:
@@ -1596,7 +1599,7 @@ def bitfield(
15961599
return BitFieldOperation(self, key, default_overflow=default_overflow)
15971600

15981601
def bitfield_ro(
1599-
self: Union["Redis", "AsyncRedis"],
1602+
self: Union["redis.client.Redis", "redis.asyncio.client.Redis"],
16001603
key: KeyT,
16011604
encoding: str,
16021605
offset: BitfieldOffsetT,
@@ -5464,27 +5467,23 @@ class Script:
54645467
An executable Lua script object returned by ``register_script``
54655468
"""
54665469

5467-
def __init__(self, registered_client: "Redis", script: ScriptTextT):
5470+
def __init__(self, registered_client: "redis.client.Redis", script: ScriptTextT):
54685471
self.registered_client = registered_client
54695472
self.script = script
54705473
# Precalculate and store the SHA1 hex digest of the script.
54715474

54725475
if isinstance(script, str):
54735476
# We need the encoding from the client in order to generate an
54745477
# accurate byte representation of the script
5475-
try:
5476-
encoder = registered_client.connection_pool.get_encoder()
5477-
except AttributeError:
5478-
# Cluster
5479-
encoder = registered_client.get_encoder()
5478+
encoder = self.get_encoder()
54805479
script = encoder.encode(script)
54815480
self.sha = hashlib.sha1(script).hexdigest()
54825481

54835482
def __call__(
54845483
self,
54855484
keys: Union[Sequence[KeyT], None] = None,
54865485
args: Union[Iterable[EncodableT], None] = None,
5487-
client: Union["Redis", None] = None,
5486+
client: Union["redis.client.Redis", None] = None,
54885487
):
54895488
"""Execute the script, passing any required ``args``"""
54905489
keys = keys or []
@@ -5507,13 +5506,35 @@ def __call__(
55075506
self.sha = client.script_load(self.script)
55085507
return client.evalsha(self.sha, len(keys), *args)
55095508

5509+
def get_encoder(self):
5510+
"""Get the encoder to encode string scripts into bytes."""
5511+
try:
5512+
return self.registered_client.get_encoder()
5513+
except AttributeError:
5514+
# DEPRECATED
5515+
# In version <=4.1.2, this was the code we used to get the encoder.
5516+
# However, after 4.1.2 we added support for scripting in clustered
5517+
# redis. ClusteredRedis doesn't have a `.connection_pool` attribute
5518+
# so we changed the Script class to use
5519+
# `self.registered_client.get_encoder` (see above).
5520+
# However, that is technically a breaking change, as consumers who
5521+
# use Scripts directly might inject a `registered_client` that
5522+
# doesn't have a `.get_encoder` field. This try/except prevents us
5523+
# from breaking backward-compatibility. Ideally, it would be
5524+
# removed in the next major release.
5525+
return self.registered_client.connection_pool.get_encoder()
5526+
55105527

55115528
class AsyncScript:
55125529
"""
55135530
An executable Lua script object returned by ``register_script``
55145531
"""
55155532

5516-
def __init__(self, registered_client: "AsyncRedis", script: ScriptTextT):
5533+
def __init__(
5534+
self,
5535+
registered_client: "redis.asyncio.client.Redis",
5536+
script: ScriptTextT,
5537+
):
55175538
self.registered_client = registered_client
55185539
self.script = script
55195540
# Precalculate and store the SHA1 hex digest of the script.
@@ -5533,7 +5554,7 @@ async def __call__(
55335554
self,
55345555
keys: Union[Sequence[KeyT], None] = None,
55355556
args: Union[Iterable[EncodableT], None] = None,
5536-
client: Union["AsyncRedis", None] = None,
5557+
client: Union["redis.asyncio.client.Redis", None] = None,
55375558
):
55385559
"""Execute the script, passing any required ``args``"""
55395560
keys = keys or []
@@ -5758,7 +5779,7 @@ def script_load(self, script: ScriptTextT) -> ResponseT:
57585779
"""
57595780
return self.execute_command("SCRIPT LOAD", script)
57605781

5761-
def register_script(self: "Redis", script: ScriptTextT) -> Script:
5782+
def register_script(self: "redis.client.Redis", script: ScriptTextT) -> Script:
57625783
"""
57635784
Register a Lua ``script`` specifying the ``keys`` it will touch.
57645785
Returns a Script object that is callable and hides the complexity of
@@ -5772,7 +5793,10 @@ class AsyncScriptCommands(ScriptCommands):
57725793
async def script_debug(self, *args) -> None:
57735794
return super().script_debug()
57745795

5775-
def register_script(self: "AsyncRedis", script: ScriptTextT) -> AsyncScript:
5796+
def register_script(
5797+
self: "redis.asyncio.client.Redis",
5798+
script: ScriptTextT,
5799+
) -> AsyncScript:
57765800
"""
57775801
Register a Lua ``script`` specifying the ``keys`` it will touch.
57785802
Returns a Script object that is callable and hides the complexity of
@@ -6283,62 +6307,6 @@ def command(self) -> ResponseT:
62836307
return self.execute_command("COMMAND")
62846308

62856309

6286-
class Script:
6287-
"""
6288-
An executable Lua script object returned by ``register_script``
6289-
"""
6290-
6291-
def __init__(self, registered_client, script):
6292-
self.registered_client = registered_client
6293-
self.script = script
6294-
# Precalculate and store the SHA1 hex digest of the script.
6295-
6296-
if isinstance(script, str):
6297-
# We need the encoding from the client in order to generate an
6298-
# accurate byte representation of the script
6299-
encoder = self.get_encoder()
6300-
script = encoder.encode(script)
6301-
self.sha = hashlib.sha1(script).hexdigest()
6302-
6303-
def __call__(self, keys=[], args=[], client=None):
6304-
"Execute the script, passing any required ``args``"
6305-
if client is None:
6306-
client = self.registered_client
6307-
args = tuple(keys) + tuple(args)
6308-
# make sure the Redis server knows about the script
6309-
from redis.client import Pipeline
6310-
6311-
if isinstance(client, Pipeline):
6312-
# Make sure the pipeline can register the script before executing.
6313-
client.scripts.add(self)
6314-
try:
6315-
return client.evalsha(self.sha, len(keys), *args)
6316-
except NoScriptError:
6317-
# Maybe the client is pointed to a different server than the client
6318-
# that created this instance?
6319-
# Overwrite the sha just in case there was a discrepancy.
6320-
self.sha = client.script_load(self.script)
6321-
return client.evalsha(self.sha, len(keys), *args)
6322-
6323-
def get_encoder(self):
6324-
"""Get the encoder to encode string scripts into bytes."""
6325-
try:
6326-
return self.registered_client.get_encoder()
6327-
except AttributeError:
6328-
# DEPRECATED
6329-
# In version <=4.1.2, this was the code we used to get the encoder.
6330-
# However, after 4.1.2 we added support for scripting in clustered
6331-
# redis. ClusteredRedis doesn't have a `.connection_pool` attribute
6332-
# so we changed the Script class to use
6333-
# `self.registered_client.get_encoder` (see above).
6334-
# However, that is technically a breaking change, as consumers who
6335-
# use Scripts directly might inject a `registered_client` that
6336-
# doesn't have a `.get_encoder` field. This try/except prevents us
6337-
# from breaking backward-compatibility. Ideally, it would be
6338-
# removed in the next major release.
6339-
return self.registered_client.connection_pool.get_encoder()
6340-
6341-
63426310
class AsyncModuleCommands(ModuleCommands):
63436311
async def command_info(self) -> None:
63446312
return super().command_info()
@@ -6415,9 +6383,12 @@ def function_list(
64156383
) -> Union[Awaitable[List], List]:
64166384
"""
64176385
Return information about the functions and libraries.
6418-
:param library: pecify a pattern for matching library names
6419-
:param withcode: cause the server to include the libraries source
6420-
implementation in the reply
6386+
6387+
Args:
6388+
6389+
library: specify a pattern for matching library names
6390+
withcode: cause the server to include the libraries source implementation
6391+
in the reply
64216392
"""
64226393
args = ["LIBRARYNAME", library]
64236394
if withcode:

0 commit comments

Comments
 (0)