diff options
Diffstat (limited to 'redis')
-rw-r--r-- | redis/__init__.py | 12 | ||||
-rwxr-xr-x | redis/client.py | 4 | ||||
-rw-r--r-- | redis/commands/core.py | 27 | ||||
-rwxr-xr-x | redis/connection.py | 50 |
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 |