summaryrefslogtreecommitdiff
path: root/redis
diff options
context:
space:
mode:
Diffstat (limited to 'redis')
-rw-r--r--redis/__init__.py12
-rwxr-xr-xredis/client.py4
-rw-r--r--redis/commands/core.py27
-rwxr-xr-xredis/connection.py50
4 files changed, 83 insertions, 10 deletions
diff --git a/redis/__init__.py b/redis/__init__.py
index 051b039..35044be 100644
--- a/redis/__init__.py
+++ b/redis/__init__.py
@@ -1,3 +1,10 @@
+import sys
+
+if sys.version_info >= (3, 8):
+ from importlib import metadata
+else:
+ import importlib_metadata as metadata
+
from redis.client import Redis, StrictRedis
from redis.cluster import RedisCluster
from redis.connection import (
@@ -38,7 +45,10 @@ def int_or_str(value):
return value
-__version__ = "4.1.0rc2"
+try:
+ __version__ = metadata.version("redis")
+except metadata.PackageNotFoundError:
+ __version__ = "99.99.99"
VERSION = tuple(map(int_or_str, __version__.split(".")))
diff --git a/redis/client.py b/redis/client.py
index c02bc3a..ae4fae2 100755
--- a/redis/client.py
+++ b/redis/client.py
@@ -873,7 +873,9 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
ssl_certfile=None,
ssl_cert_reqs="required",
ssl_ca_certs=None,
+ ssl_ca_path=None,
ssl_check_hostname=False,
+ ssl_password=None,
max_connections=None,
single_connection_client=False,
health_check_interval=0,
@@ -947,6 +949,8 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
"ssl_cert_reqs": ssl_cert_reqs,
"ssl_ca_certs": ssl_ca_certs,
"ssl_check_hostname": ssl_check_hostname,
+ "ssl_password": ssl_password,
+ "ssl_ca_path": ssl_ca_path,
}
)
connection_pool = ConnectionPool(**kwargs)
diff --git a/redis/commands/core.py b/redis/commands/core.py
index fec3095..835ea61 100644
--- a/redis/commands/core.py
+++ b/redis/commands/core.py
@@ -637,6 +637,31 @@ class ManagementCommands:
args.append(b"ASYNC")
return self.execute_command("FLUSHDB", *args, **kwargs)
+ def sync(self):
+ """
+ Initiates a replication stream from the master.
+
+ For more information check https://redis.io/commands/sync
+ """
+ from redis.client import NEVER_DECODE
+
+ options = {}
+ options[NEVER_DECODE] = []
+ return self.execute_command("SYNC", **options)
+
+ def psync(self, replicationid, offset):
+ """
+ Initiates a replication stream from the master.
+ Newer version for `sync`.
+
+ For more information check https://redis.io/commands/sync
+ """
+ from redis.client import NEVER_DECODE
+
+ options = {}
+ options[NEVER_DECODE] = []
+ return self.execute_command("PSYNC", replicationid, offset, **options)
+
def swapdb(self, first, second, **kwargs):
"""
Swap two databases
@@ -1244,7 +1269,7 @@ class BasicKeyCommands:
pushing it as the first/last element on the destination list.
Returns the element being popped and pushed.
- For more information check https://redis.io/commands/lmov
+ For more information check https://redis.io/commands/lmove
"""
params = [first_list, second_list, src, dest]
return self.execute_command("LMOVE", *params)
diff --git a/redis/connection.py b/redis/connection.py
index 2001c64..3fe8543 100755
--- a/redis/connection.py
+++ b/redis/connection.py
@@ -382,7 +382,7 @@ class HiredisParser(BaseParser):
except Exception:
pass
- def on_connect(self, connection):
+ def on_connect(self, connection, **kwargs):
self._sock = connection._sock
self._socket_timeout = connection.socket_timeout
kwargs = {
@@ -552,8 +552,8 @@ class Connection:
self.retry = Retry(NoBackoff(), 0)
self.health_check_interval = health_check_interval
self.next_health_check = 0
- self.encoder = Encoder(encoding, encoding_errors, decode_responses)
self.redis_connect_func = redis_connect_func
+ self.encoder = Encoder(encoding, encoding_errors, decode_responses)
self._sock = None
self._socket_read_size = socket_read_size
self.set_parser(parser_class)
@@ -717,9 +717,14 @@ class Connection:
self._parser.on_disconnect()
if self._sock is None:
return
- try:
- if os.getpid() == self.pid:
+
+ if os.getpid() == self.pid:
+ try:
self._sock.shutdown(socket.SHUT_RDWR)
+ except OSError:
+ pass
+
+ try:
self._sock.close()
except OSError:
pass
@@ -879,6 +884,11 @@ class Connection:
class SSLConnection(Connection):
+ """Manages SSL connections to and from the Redis server(s).
+ This class extends the Connection class, adding SSL functionality, and making
+ use of ssl.SSLContext (https://docs.python.org/3/library/ssl.html#ssl.SSLContext)
+ """ # noqa
+
def __init__(
self,
ssl_keyfile=None,
@@ -886,8 +896,24 @@ class SSLConnection(Connection):
ssl_cert_reqs="required",
ssl_ca_certs=None,
ssl_check_hostname=False,
+ ssl_ca_path=None,
+ ssl_password=None,
**kwargs,
):
+ """Constructor
+
+ Args:
+ ssl_keyfile: Path to an ssl private key. Defaults to None.
+ ssl_certfile: Path to an ssl certificate. Defaults to None.
+ ssl_cert_reqs: The string value for the SSLContext.verify_mode (none, optional, required). Defaults to "required".
+ ssl_ca_certs: The path to a file of concatenated CA certificates in PEM format. Defaults to None.
+ ssl_check_hostname: If set, match the hostname during the SSL handshake. Defaults to False.
+ ssl_ca_path: The path to a directory containing several CA certificates in PEM format. Defaults to None.
+ ssl_password: Password for unlocking an encrypted private key. Defaults to None.
+
+ Raises:
+ RedisError
+ """ # noqa
if not ssl_available:
raise RedisError("Python wasn't built with SSL support")
@@ -910,7 +936,9 @@ class SSLConnection(Connection):
ssl_cert_reqs = CERT_REQS[ssl_cert_reqs]
self.cert_reqs = ssl_cert_reqs
self.ca_certs = ssl_ca_certs
+ self.ca_path = ssl_ca_path
self.check_hostname = ssl_check_hostname
+ self.certificate_password = ssl_password
def _connect(self):
"Wrap the socket with SSL support"
@@ -918,10 +946,14 @@ class SSLConnection(Connection):
context = ssl.create_default_context()
context.check_hostname = self.check_hostname
context.verify_mode = self.cert_reqs
- if self.certfile and self.keyfile:
- context.load_cert_chain(certfile=self.certfile, keyfile=self.keyfile)
- if self.ca_certs:
- context.load_verify_locations(self.ca_certs)
+ if self.certfile or self.keyfile:
+ context.load_cert_chain(
+ certfile=self.certfile,
+ keyfile=self.keyfile,
+ password=self.certificate_password,
+ )
+ if self.ca_certs is not None or self.ca_path is not None:
+ context.load_verify_locations(cafile=self.ca_certs, capath=self.ca_path)
return context.wrap_socket(sock, server_hostname=self.host)
@@ -942,6 +974,7 @@ class UnixDomainSocketConnection(Connection):
health_check_interval=0,
client_name=None,
retry=None,
+ redis_connect_func=None,
):
"""
Initialize a new UnixDomainSocketConnection.
@@ -966,6 +999,7 @@ class UnixDomainSocketConnection(Connection):
self.retry = Retry(NoBackoff(), 0)
self.health_check_interval = health_check_interval
self.next_health_check = 0
+ self.redis_connect_func = redis_connect_func
self.encoder = Encoder(encoding, encoding_errors, decode_responses)
self._sock = None
self._socket_read_size = socket_read_size