Skip to content
Merged
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
3 changes: 3 additions & 0 deletions pyredis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ def get_by_url(
Args:
url: The URL containing connection information and options.
async_client: If True, returns the asynchronous pool or client variant.

Returns:
A connection pool or client instance matching the scheme and configuration.
"""
scheme, rest = url.split("://", 1)
conns = list()
Expand Down
17 changes: 17 additions & 0 deletions pyredis/client/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,33 @@ class AsyncClient(
"""

def __init__(self, **kwargs):
"""
Initialize the AsyncClient connection.

Args:
**kwargs: Connection options forwarded to AsyncConnection.
"""
super().__init__()
self._conn = AsyncConnection(**kwargs)

async def execute(self, *args):
"""
Asynchronously execute a Redis command.

Args:
*args: Command name and positional arguments.

Returns:
Parsed Redis reply.
"""
await self._conn.write(*args)
return await self._conn.read()

async def close(self):
"""Asynchronously close the underlying connection."""
await self._conn.close()

@property
def closed(self):
"""Flag indicating if connection is closed."""
return self._conn.closed
32 changes: 32 additions & 0 deletions pyredis/client/async_cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@ def __init__(
cluster_map=None,
username=None,
):
"""
Initialize the AsyncClusterClient.

Args:
seeds: List of (host, port) tuples representing cluster seeds.
database: Redis database index (default: 0).
password: Optional password for Redis authentication.
encoding: Optional string encoding for decoding responses.
slave_ok: If True, allows routing read commands to replicas.
conn_timeout: Connection timeout in seconds.
read_timeout: Read timeout in seconds.
cluster_map: Optional pre-configured AsyncClusterMap instance.
username: Optional username for Redis ACL authentication.
"""
super().__init__()
if not bool(seeds) != bool(cluster_map):
raise PyRedisError("Ether seeds or cluster_map has to be provided")
Expand Down Expand Up @@ -101,6 +115,11 @@ async def _get_slot_info(self, shard_key):

@property
def closed(self):
"""
Flag indicating if all cluster connections are closed.

Always returns False for AsyncClusterClient.
"""
return False

async def execute(
Expand All @@ -111,6 +130,19 @@ async def execute(
asking=False,
retries=3
):
"""
Execute a Redis command on the appropriate cluster node asynchronously.

Args:
*args: Command name and arguments.
shard_key: Key used to determine the slot and node for routing.
sock: Optional explicit socket identifier (host_port) to route to.
asking: Flag indicating if this is an ASKING command redirection.
retries: Number of retries on slot redirection before failing.

Returns:
The Redis command response.
"""
if not bool(shard_key) != bool(sock):
raise PyRedisError("Ether shard_key or sock has to be provided")
if not sock:
Expand Down
39 changes: 39 additions & 0 deletions pyredis/client/async_hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ def __init__(
read_timeout=2,
username=None,
):
"""
Initialize the AsyncHashClient.

Args:
buckets: List of (host, port) tuples representing the server buckets.
database: Optional Redis database index.
password: Optional password for Redis authentication.
encoding: Optional string encoding for decoding responses.
conn_timeout: Connection timeout in seconds.
read_timeout: Read timeout in seconds.
username: Optional username for Redis ACL authentication.
"""
super().__init__()
self._conns = dict()
self._conn_names = list()
Expand Down Expand Up @@ -116,9 +128,17 @@ def _init_map(self):

@property
def bulk(self):
"""Flag indicating if bulk mode is active."""
return self._bulk

def bulk_start(self, bulk_size=5000, keep_results=True):
"""
Start bulk command pipelining mode.

Args:
bulk_size: Maximum commands to queue before reading responses.
keep_results: Flag indicating if responses should be returned.
"""
if self.bulk:
raise PyRedisError("Already in bulk mode")
self._bulk = True
Expand All @@ -129,6 +149,12 @@ def bulk_start(self, bulk_size=5000, keep_results=True):
self._bulk_keep = True

async def bulk_stop(self):
"""
Stop bulk mode and retrieve results asynchronously.

Returns:
List of execution results if keep_results was set.
"""
if not self.bulk:
raise PyRedisError("Not in bulk mode")
await self._bulk_fetch()
Expand All @@ -141,15 +167,28 @@ async def bulk_stop(self):
return results

async def close(self):
"""Close all connections to the server buckets asynchronously."""
for conn in self._conns.values():
await conn.close()
self._closed = True

@property
def closed(self):
"""Flag indicating if the client connections are closed."""
return self._closed

async def execute(self, *args, shard_key=None, sock=None):
"""
Execute a command on the appropriate bucket asynchronously.

Args:
*args: Command name and arguments.
shard_key: Key used to calculate the slot and route the command.
sock: Optional explicit socket name/bucket to route to.

Returns:
The Redis command response, or None if in bulk mode.
"""
if not bool(shard_key) != bool(sock):
raise PyRedisError("Ether shard_key or sock has to be provided")
if not sock:
Expand Down
20 changes: 20 additions & 0 deletions pyredis/client/async_pubsub.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,37 @@ class AsyncPubSubClient(commands.Subscribe):
"""

def __init__(self, **kwargs):
"""
Initialize the AsyncPubSubClient connection.

Args:
**kwargs: Connection options forwarded to AsyncConnection.
"""
self._conn = pyredis.client.AsyncConnection(**kwargs)

async def close(self):
"""Close the underlying connection asynchronously."""
await self._conn.close()

@property
def closed(self):
"""Flag indicating if the connection is closed."""
return self._conn.closed

async def write(self, *args):
"""
Write a command to the underlying connection asynchronously.

Args:
*args: Command name and arguments.
"""
return await self._conn.write(*args)

async def get(self):
"""
Read the next message/response from the connection asynchronously.

Returns:
The message or response read from Redis.
"""
return await self._conn.read(close_on_timeout=False)
44 changes: 44 additions & 0 deletions pyredis/client/async_sentinel.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ class AsyncSentinelClient(object):
"""

def __init__(self, sentinels, password=None, username=None):
"""
Initialize the AsyncSentinelClient.

Args:
sentinels: List of (host, port) tuples representing the Sentinel nodes.
password: Optional password for Sentinel authentication.
username: Optional username for Sentinel ACL authentication.
"""
self._conn = None
self._sentinels = deque(sentinels)
self._password = password
Expand Down Expand Up @@ -42,27 +50,53 @@ async def _sentinel_get(self):
raise PyRedisConnError("Could not connect to any sentinel")

async def close(self):
"""Close the active Sentinel connection asynchronously."""
if self._conn:
await self._conn.close()
self._conn = None

@property
def sentinels(self):
"""Deque of configured Sentinel (host, port) node addresses."""
return self._sentinels

async def execute(self, *args):
"""
Execute a Sentinel command asynchronously, automatically selecting an active Sentinel node.

Args:
*args: Command name and arguments.

Returns:
The Sentinel command response.
"""
if not self._conn:
await self._sentinel_get()
await self._conn.write(*args)
return await self._conn.read()

async def get_master(self, name):
"""
Get the master node configuration for the specified service name asynchronously.

Args:
name: The service name of the Redis master.

Returns:
Dict containing the master's configuration.
"""
result = await self.execute(
*["SENTINEL", "master", name]
)
return pyredis.client.dict_from_list(result)

async def get_masters(self):
"""
Get configurations for all monitored Redis master nodes asynchronously.

Returns:
List of dicts containing configurations for all monitored masters.
"""
masters = await self.execute(
*["SENTINEL", "masters"]
)
Expand All @@ -74,6 +108,15 @@ async def get_masters(self):
return result

async def get_slaves(self, name):
"""
Get replication replica configurations for the specified master service name asynchronously.

Args:
name: The service name of the Redis master.

Returns:
List of dicts containing replica configurations.
"""
slaves = await self.execute(
*["SENTINEL", "slaves", name]
)
Expand All @@ -85,5 +128,6 @@ async def get_slaves(self, name):
return result

async def next_sentinel(self):
"""Close the active connection and rotate the Sentinel node list asynchronously."""
await self.close()
self._sentinels.rotate(-1)
31 changes: 31 additions & 0 deletions pyredis/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ class Client(
"""

def __init__(self, **kwargs):
"""
Initialize the Client connection.

Args:
**kwargs: Connection options forwarded to Connection.
"""
super().__init__()
self._conn = pyredis.client.Connection(**kwargs)
self._bulk = False
Expand Down Expand Up @@ -52,9 +58,17 @@ def _execute_bulk(self, *args):

@property
def bulk(self):
"""Flag indicating if bulk mode is active."""
return self._bulk

def bulk_start(self, bulk_size=5000, keep_results=True):
"""
Start bulk command pipelining mode.

Args:
bulk_size: Maximum commands to queue before reading responses.
keep_results: Flag indicating if responses should be returned.
"""
if self.bulk:
raise PyRedisError("Already in bulk mode")
self._bulk = True
Expand All @@ -65,6 +79,12 @@ def bulk_start(self, bulk_size=5000, keep_results=True):
self._bulk_keep = True

def bulk_stop(self):
"""
Stop bulk mode and retrieve results.

Returns:
List of execution results if keep_results was set.
"""
if not self.bulk:
raise PyRedisError("Not in bulk mode")
self._bulk_fetch()
Expand All @@ -77,13 +97,24 @@ def bulk_stop(self):
return results

def close(self):
"""Close the underlying connection."""
self._conn.close()

@property
def closed(self):
"""Flag indicating if connection is closed."""
return self._conn.closed

def execute(self, *args):
"""
Execute a Redis command.

Args:
*args: Command name and positional arguments.

Returns:
Parsed Redis reply.
"""
if not self._bulk:
return self._execute_basic(*args)
else:
Expand Down
Loading
Loading