Skip to content

Commit b3cd82b

Browse files
Merge remote-tracking branch 'original/master'
2 parents a5f6e4b + 40fdb15 commit b3cd82b

19 files changed

+6929
-17
lines changed

CHANGES

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11

2+
* Allow negative `retries` for `Retry` class to retry forever
23
* Add `items` parameter to `hset` signature
34
* Create codeql-analysis.yml (#1988). Thanks @chayim
45
* Add limited support for Lua scripting with RedisCluster
@@ -7,6 +8,7 @@
78
* Fix scan_iter for RedisCluster
89
* Remove verbose logging when initializing ClusterPubSub, ClusterPipeline or RedisCluster
910
* Fix broken connection writer lock-up for asyncio (#2065)
11+
* Fix auth bug when provided with no username (#2086)
1012

1113
* 4.1.3 (Feb 8, 2022)
1214
* Fix flushdb and flushall (#1926)

redis/asyncio/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
)
4242
from redis.commands import (
4343
AsyncCoreCommands,
44+
AsyncRedisModuleCommands,
4445
AsyncSentinelCommands,
45-
RedisModuleCommands,
4646
list_or_args,
4747
)
4848
from redis.compat import Protocol, TypedDict
@@ -81,7 +81,7 @@ async def __call__(self, response: Any, **kwargs):
8181

8282

8383
class Redis(
84-
AbstractRedis, RedisModuleCommands, AsyncCoreCommands, AsyncSentinelCommands
84+
AbstractRedis, AsyncRedisModuleCommands, AsyncCoreCommands, AsyncSentinelCommands
8585
):
8686
"""
8787
Implementation of the Redis protocol.

redis/asyncio/retry.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def __init__(
2727
"""
2828
Initialize a `Retry` object with a `Backoff` object
2929
that retries a maximum of `retries` times.
30+
`retries` can be negative to retry forever.
3031
You can specify the types of supported errors which trigger
3132
a retry with the `supported_errors` parameter.
3233
"""
@@ -51,7 +52,7 @@ async def call_with_retry(
5152
except self._supported_errors as error:
5253
failures += 1
5354
await fail(error)
54-
if failures > self._retries:
55+
if self._retries >= 0 and failures > self._retries:
5556
raise error
5657
backoff = self._backoff.compute(failures)
5758
if backoff > 0:

redis/cluster.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -802,7 +802,10 @@ def set_response_callback(self, command, callback):
802802
self.cluster_response_callbacks[command] = callback
803803

804804
def _determine_nodes(self, *args, **kwargs):
805-
command = args[0]
805+
command = args[0].upper()
806+
if len(args) >= 2 and f"{args[0]} {args[1]}".upper() in self.command_flags:
807+
command = f"{args[0]} {args[1]}".upper()
808+
806809
nodes_flag = kwargs.pop("nodes_flag", None)
807810
if nodes_flag is not None:
808811
# nodes flag passed by the user

redis/commands/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from .core import AsyncCoreCommands, CoreCommands
33
from .helpers import list_or_args
44
from .parser import CommandsParser
5-
from .redismodules import RedisModuleCommands
5+
from .redismodules import AsyncRedisModuleCommands, RedisModuleCommands
66
from .sentinel import AsyncSentinelCommands, SentinelCommands
77

88
__all__ = [
@@ -12,6 +12,7 @@
1212
"AsyncCoreCommands",
1313
"CoreCommands",
1414
"list_or_args",
15+
"AsyncRedisModuleCommands",
1516
"RedisModuleCommands",
1617
"AsyncSentinelCommands",
1718
"SentinelCommands",

redis/commands/cluster.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -315,19 +315,19 @@ def replicaof(self, *args, **kwargs) -> NoReturn:
315315
316316
For more information see https://redis.io/commands/replicaof
317317
"""
318-
raise RedisClusterException("REPLICAOF is not supported in cluster" " mode")
318+
raise RedisClusterException("REPLICAOF is not supported in cluster mode")
319319

320320
def swapdb(self, *args, **kwargs) -> NoReturn:
321321
"""
322322
Swaps two Redis databases.
323323
324324
For more information see https://redis.io/commands/swapdb
325325
"""
326-
raise RedisClusterException("SWAPDB is not supported in cluster" " mode")
326+
raise RedisClusterException("SWAPDB is not supported in cluster mode")
327327

328328
def cluster_myid(self, target_node: "TargetNodesT") -> ResponseT:
329329
"""
330-
Returns the nodes id.
330+
Returns the node's id.
331331
332332
:target_node: 'ClusterNode'
333333
The node to execute the command on
@@ -805,7 +805,6 @@ class RedisClusterCommands(
805805
target specific nodes. By default, if target_nodes is not specified, the
806806
command will be executed on the default cluster node.
807807
808-
809808
:param :target_nodes: type can be one of the followings:
810809
- nodes flag: ALL_NODES, PRIMARIES, REPLICAS, RANDOM
811810
- 'ClusterNode'
@@ -834,7 +833,6 @@ class AsyncRedisClusterCommands(
834833
target specific nodes. By default, if target_nodes is not specified, the
835834
command will be executed on the default cluster node.
836835
837-
838836
:param :target_nodes: type can be one of the followings:
839837
- nodes flag: ALL_NODES, PRIMARIES, REPLICAS, RANDOM
840838
- 'ClusterNode'

redis/commands/core.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -376,9 +376,11 @@ def auth(self, password, username=None, **kwargs):
376376
authenticate for the given user.
377377
For more information see https://redis.io/commands/auth
378378
"""
379-
if username:
380-
return self.execute_command("AUTH", username, password, **kwargs)
381-
return self.execute_command
379+
pieces = []
380+
if username is not None:
381+
pieces.append(username)
382+
pieces.append(password)
383+
return self.execute_command("AUTH", *pieces, **kwargs)
382384

383385
def bgrewriteaof(self, **kwargs):
384386
"""Tell the Redis server to rewrite the AOF file from data in memory.
@@ -3817,7 +3819,7 @@ def xrevrange(
38173819
def xtrim(
38183820
self,
38193821
name: KeyT,
3820-
maxlen: int,
3822+
maxlen: Union[int, None],
38213823
approximate: bool = True,
38223824
minid: Union[StreamIdT, None] = None,
38233825
limit: Union[int, None] = None,

redis/commands/redismodules.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,13 @@ def graph(self, index_name="idx"):
8181

8282
g = Graph(client=self, name=index_name)
8383
return g
84+
85+
86+
class AsyncRedisModuleCommands(RedisModuleCommands):
87+
def ft(self, index_name="idx"):
88+
"""Access the search namespace, providing support for redis search."""
89+
90+
from .search import AsyncSearch
91+
92+
s = AsyncSearch(client=self, index_name=index_name)
93+
return s

redis/commands/search/__init__.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import redis
22

3-
from .commands import SearchCommands
3+
from .commands import AsyncSearchCommands, SearchCommands
44

55

66
class Search(SearchCommands):
@@ -112,5 +112,67 @@ def pipeline(self, transaction=True, shard_hint=None):
112112
return p
113113

114114

115+
class AsyncSearch(Search, AsyncSearchCommands):
116+
class BatchIndexer(Search.BatchIndexer):
117+
"""
118+
A batch indexer allows you to automatically batch
119+
document indexing in pipelines, flushing it every N documents.
120+
"""
121+
122+
async def add_document(
123+
self,
124+
doc_id,
125+
nosave=False,
126+
score=1.0,
127+
payload=None,
128+
replace=False,
129+
partial=False,
130+
no_create=False,
131+
**fields,
132+
):
133+
"""
134+
Add a document to the batch query
135+
"""
136+
self.client._add_document(
137+
doc_id,
138+
conn=self._pipeline,
139+
nosave=nosave,
140+
score=score,
141+
payload=payload,
142+
replace=replace,
143+
partial=partial,
144+
no_create=no_create,
145+
**fields,
146+
)
147+
self.current_chunk += 1
148+
self.total += 1
149+
if self.current_chunk >= self.chunk_size:
150+
await self.commit()
151+
152+
async def commit(self):
153+
"""
154+
Manually commit and flush the batch indexing query
155+
"""
156+
await self._pipeline.execute()
157+
self.current_chunk = 0
158+
159+
def pipeline(self, transaction=True, shard_hint=None):
160+
"""Creates a pipeline for the SEARCH module, that can be used for executing
161+
SEARCH commands, as well as classic core commands.
162+
"""
163+
p = AsyncPipeline(
164+
connection_pool=self.client.connection_pool,
165+
response_callbacks=self.MODULE_CALLBACKS,
166+
transaction=transaction,
167+
shard_hint=shard_hint,
168+
)
169+
p.index_name = self.index_name
170+
return p
171+
172+
115173
class Pipeline(SearchCommands, redis.client.Pipeline):
116174
"""Pipeline for the module."""
175+
176+
177+
class AsyncPipeline(AsyncSearchCommands, redis.asyncio.client.Pipeline):
178+
"""AsyncPipeline for the module."""

0 commit comments

Comments
 (0)