summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAarni Koskela <akx@iki.fi>2021-11-30 17:20:43 +0200
committerGitHub <noreply@github.com>2021-11-30 17:20:43 +0200
commitfbe87acf96aab583975ed3371423eb20602eccaf (patch)
treeb873c3356b9c96e826c4ab0bb3efc339a00838e4
parent4db85ef574a64a2b230a3ae1ff19c9d04065a114 (diff)
downloadredis-py-fbe87acf96aab583975ed3371423eb20602eccaf.tar.gz
Pyupgrade + flynt + f-strings (#1759)
@akx Thank you so much for this! Thanks again for introducing me to a new tool that I'm sliding into my workflow as well.
-rw-r--r--benchmarks/base.py6
-rw-r--r--benchmarks/basic_operations.py23
-rw-r--r--benchmarks/command_packer_benchmark.py11
-rw-r--r--dev_requirements.txt1
-rwxr-xr-xredis/client.py18
-rw-r--r--redis/cluster.py134
-rw-r--r--redis/commands/cluster.py20
-rw-r--r--redis/commands/core.py45
-rw-r--r--redis/commands/helpers.py2
-rw-r--r--redis/commands/json/path.py2
-rw-r--r--redis/commands/parser.py5
-rw-r--r--redis/commands/search/__init__.py2
-rw-r--r--redis/commands/search/aggregation.py27
-rw-r--r--redis/commands/search/commands.py2
-rw-r--r--redis/commands/search/document.py4
-rw-r--r--redis/commands/search/field.py2
-rw-r--r--redis/commands/search/indexDefinition.py5
-rw-r--r--redis/commands/search/query.py10
-rw-r--r--redis/commands/search/querystring.py22
-rw-r--r--redis/commands/search/reducers.py24
-rw-r--r--redis/commands/search/result.py4
-rw-r--r--redis/commands/search/suggestion.py4
-rw-r--r--redis/commands/timeseries/info.py2
-rwxr-xr-xredis/connection.py65
-rw-r--r--redis/sentinel.py28
-rw-r--r--tasks.py4
-rw-r--r--tests/conftest.py20
-rw-r--r--tests/test_cluster.py23
-rw-r--r--tests/test_commands.py32
-rw-r--r--tests/test_connection.py2
-rw-r--r--tests/test_multiprocessing.py7
-rw-r--r--tests/test_pipeline.py2
-rw-r--r--tests/test_pubsub.py2
-rw-r--r--tests/test_search.py4
-rw-r--r--tests/test_timeseries.py2
-rw-r--r--tox.ini3
36 files changed, 263 insertions, 306 deletions
diff --git a/benchmarks/base.py b/benchmarks/base.py
index 8c13afe..519c9cc 100644
--- a/benchmarks/base.py
+++ b/benchmarks/base.py
@@ -34,12 +34,12 @@ class Benchmark:
group_values = [group['values'] for group in self.ARGUMENTS]
for value_set in itertools.product(*group_values):
pairs = list(zip(group_names, value_set))
- arg_string = ', '.join(['%s=%s' % (p[0], p[1]) for p in pairs])
- sys.stdout.write('Benchmark: %s... ' % arg_string)
+ arg_string = ', '.join(f'{p[0]}={p[1]}' for p in pairs)
+ sys.stdout.write(f'Benchmark: {arg_string}... ')
sys.stdout.flush()
kwargs = dict(pairs)
setup = functools.partial(self.setup, **kwargs)
run = functools.partial(self.run, **kwargs)
t = timeit.timeit(stmt=run, setup=setup, number=1000)
- sys.stdout.write('%f\n' % t)
+ sys.stdout.write(f'{t:f}\n')
sys.stdout.flush()
diff --git a/benchmarks/basic_operations.py b/benchmarks/basic_operations.py
index 9446343..cb009de 100644
--- a/benchmarks/basic_operations.py
+++ b/benchmarks/basic_operations.py
@@ -49,9 +49,9 @@ def timer(func):
count = kwargs['num']
else:
count = args[1]
- print('{} - {} Requests'.format(func.__name__, count))
- print('Duration = {}'.format(duration))
- print('Rate = {}'.format(count/duration))
+ print(f'{func.__name__} - {count} Requests')
+ print(f'Duration = {duration}')
+ print(f'Rate = {count/duration}')
print()
return ret
return wrapper
@@ -62,10 +62,9 @@ def set_str(conn, num, pipeline_size, data_size):
if pipeline_size > 1:
conn = conn.pipeline()
- format_str = '{:0<%d}' % data_size
- set_data = format_str.format('a')
+ set_data = 'a'.ljust(data_size, '0')
for i in range(num):
- conn.set('set_str:%d' % i, set_data)
+ conn.set(f'set_str:{i}', set_data)
if pipeline_size > 1 and i % pipeline_size == 0:
conn.execute()
@@ -78,10 +77,9 @@ def set_int(conn, num, pipeline_size, data_size):
if pipeline_size > 1:
conn = conn.pipeline()
- format_str = '{:0<%d}' % data_size
- set_data = int(format_str.format('1'))
+ set_data = 10 ** (data_size - 1)
for i in range(num):
- conn.set('set_int:%d' % i, set_data)
+ conn.set(f'set_int:{i}', set_data)
if pipeline_size > 1 and i % pipeline_size == 0:
conn.execute()
@@ -95,7 +93,7 @@ def get_str(conn, num, pipeline_size, data_size):
conn = conn.pipeline()
for i in range(num):
- conn.get('set_str:%d' % i)
+ conn.get(f'set_str:{i}')
if pipeline_size > 1 and i % pipeline_size == 0:
conn.execute()
@@ -109,7 +107,7 @@ def get_int(conn, num, pipeline_size, data_size):
conn = conn.pipeline()
for i in range(num):
- conn.get('set_int:%d' % i)
+ conn.get(f'set_int:{i}')
if pipeline_size > 1 and i % pipeline_size == 0:
conn.execute()
@@ -136,8 +134,7 @@ def lpush(conn, num, pipeline_size, data_size):
if pipeline_size > 1:
conn = conn.pipeline()
- format_str = '{:0<%d}' % data_size
- set_data = int(format_str.format('1'))
+ set_data = 10 ** (data_size - 1)
for i in range(num):
conn.lpush('lpush_key', set_data)
if pipeline_size > 1 and i % pipeline_size == 0:
diff --git a/benchmarks/command_packer_benchmark.py b/benchmarks/command_packer_benchmark.py
index 823a8c8..3176c06 100644
--- a/benchmarks/command_packer_benchmark.py
+++ b/benchmarks/command_packer_benchmark.py
@@ -1,4 +1,3 @@
-import socket
from redis.connection import (Connection, SYM_STAR, SYM_DOLLAR, SYM_EMPTY,
SYM_CRLF)
from base import Benchmark
@@ -11,14 +10,13 @@ class StringJoiningConnection(Connection):
self.connect()
try:
self._sock.sendall(command)
- except socket.error as e:
+ except OSError as e:
self.disconnect()
if len(e.args) == 1:
_errno, errmsg = 'UNKNOWN', e.args[0]
else:
_errno, errmsg = e.args
- raise ConnectionError("Error %s while writing to socket. %s." %
- (_errno, errmsg))
+ raise ConnectionError(f"Error {_errno} while writing to socket. {errmsg}.")
except Exception:
self.disconnect()
raise
@@ -43,14 +41,13 @@ class ListJoiningConnection(Connection):
command = [command]
for item in command:
self._sock.sendall(item)
- except socket.error as e:
+ except OSError as e:
self.disconnect()
if len(e.args) == 1:
_errno, errmsg = 'UNKNOWN', e.args[0]
else:
_errno, errmsg = e.args
- raise ConnectionError("Error %s while writing to socket. %s." %
- (_errno, errmsg))
+ raise ConnectionError(f"Error {_errno} while writing to socket. {errmsg}.")
except Exception:
self.disconnect()
raise
diff --git a/dev_requirements.txt b/dev_requirements.txt
index 7f099cb..56ac08e 100644
--- a/dev_requirements.txt
+++ b/dev_requirements.txt
@@ -1,4 +1,5 @@
flake8>=3.9.2
+flynt~=0.69.0
pytest==6.2.5
pytest-timeout==2.0.1
tox==3.24.4
diff --git a/redis/client.py b/redis/client.py
index 0ae64be..9f2907e 100755
--- a/redis/client.py
+++ b/redis/client.py
@@ -629,7 +629,7 @@ def parse_set_result(response, **options):
return response and str_if_bytes(response) == 'OK'
-class Redis(RedisModuleCommands, CoreCommands, SentinelCommands, object):
+class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
"""
Implementation of the Redis protocol.
@@ -918,7 +918,7 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands, object):
self.__class__.RESPONSE_CALLBACKS)
def __repr__(self):
- return "%s<%s>" % (type(self).__name__, repr(self.connection_pool))
+ return f"{type(self).__name__}<{repr(self.connection_pool)}>"
def set_response_callback(self, command, callback):
"Set a custom Response Callback"
@@ -1141,7 +1141,7 @@ class Monitor:
# check that monitor returns 'OK', but don't return it to user
response = self.connection.read_response()
if not bool_ok(response):
- raise RedisError('MONITOR failed: %s' % response)
+ raise RedisError(f'MONITOR failed: {response}')
return self
def __exit__(self, *args):
@@ -1517,12 +1517,10 @@ class PubSub:
exception_handler=None):
for channel, handler in self.channels.items():
if handler is None:
- raise PubSubError("Channel: '%s' has no handler registered" %
- channel)
+ raise PubSubError(f"Channel: '{channel}' has no handler registered")
for pattern, handler in self.patterns.items():
if handler is None:
- raise PubSubError("Pattern: '%s' has no handler registered" %
- pattern)
+ raise PubSubError(f"Pattern: '{pattern}' has no handler registered")
thread = PubSubWorkerThread(
self,
@@ -1807,8 +1805,10 @@ class Pipeline(Redis):
def annotate_exception(self, exception, number, command):
cmd = ' '.join(map(safe_str, command))
- msg = 'Command # %d (%s) of pipeline caused error: %s' % (
- number, cmd, exception.args[0])
+ msg = (
+ f'Command # {number} ({cmd}) of pipeline '
+ f'caused error: {exception.args[0]}'
+ )
exception.args = (msg,) + exception.args[1:]
def parse_response(self, connection, command_name, **options):
diff --git a/redis/cluster.py b/redis/cluster.py
index 91a4d55..c1853aa 100644
--- a/redis/cluster.py
+++ b/redis/cluster.py
@@ -42,7 +42,7 @@ log = logging.getLogger(__name__)
def get_node_name(host, port):
- return '{0}:{1}'.format(host, port)
+ return f'{host}:{port}'
def get_connection(redis_node, *args, **options):
@@ -200,7 +200,7 @@ class ClusterParser(DefaultParser):
})
-class RedisCluster(ClusterCommands, object):
+class RedisCluster(ClusterCommands):
RedisClusterRequestTTL = 16
PRIMARIES = "primaries"
@@ -451,7 +451,7 @@ class RedisCluster(ClusterCommands, object):
"2. list of startup nodes, for example:\n"
" RedisCluster(startup_nodes=[ClusterNode('localhost', 6379),"
" ClusterNode('localhost', 6378)])")
- log.debug("startup_nodes : {0}".format(startup_nodes))
+ log.debug(f"startup_nodes : {startup_nodes}")
# Update the connection arguments
# Whenever a new connection is established, RedisCluster's on_connect
# method should be run
@@ -602,7 +602,7 @@ class RedisCluster(ClusterCommands, object):
slot_cache = self.nodes_manager.slots_cache.get(slot)
if slot_cache is None or len(slot_cache) == 0:
raise SlotNotCoveredError(
- 'Slot "{0}" is not covered by the cluster.'.format(slot)
+ f'Slot "{slot}" is not covered by the cluster.'
)
if replica and len(self.nodes_manager.slots_cache[slot]) < 2:
return None
@@ -631,7 +631,7 @@ class RedisCluster(ClusterCommands, object):
"the default node was not changed.")
return False
self.nodes_manager.default_node = node
- log.info("Changed the default cluster node to {0}".format(node))
+ log.info(f"Changed the default cluster node to {node}")
return True
def pubsub(self, node=None, host=None, port=None, **kwargs):
@@ -678,8 +678,7 @@ class RedisCluster(ClusterCommands, object):
# get the nodes group for this command if it was predefined
command_flag = self.command_flags.get(command)
if command_flag:
- log.debug("Target node/s for {0}: {1}".
- format(command, command_flag))
+ log.debug(f"Target node/s for {command}: {command_flag}")
if command_flag == self.__class__.RANDOM:
# return a random node
return [self.get_random_node()]
@@ -700,7 +699,7 @@ class RedisCluster(ClusterCommands, object):
slot = self.determine_slot(*args)
node = self.nodes_manager.get_node_from_slot(
slot, self.read_from_replicas and command in READ_COMMANDS)
- log.debug("Target for {0}: slot {1}".format(args, slot))
+ log.debug(f"Target for {args}: slot {slot}")
return [node]
def _should_reinitialized(self):
@@ -741,7 +740,7 @@ class RedisCluster(ClusterCommands, object):
raise RedisClusterException(
"No way to dispatch this command to Redis Cluster. "
"Missing key.\nYou can execute the command by specifying "
- "target nodes.\nCommand: {0}".format(args)
+ f"target nodes.\nCommand: {args}"
)
if len(keys) > 1:
@@ -749,8 +748,9 @@ class RedisCluster(ClusterCommands, object):
# the same slot
slots = {self.keyslot(key) for key in keys}
if len(slots) != 1:
- raise RedisClusterException("{0} - all keys must map to the "
- "same key slot".format(args[0]))
+ raise RedisClusterException(
+ f"{args[0]} - all keys must map to the same key slot"
+ )
return slots.pop()
else:
# single key command
@@ -775,12 +775,12 @@ class RedisCluster(ClusterCommands, object):
# rc.cluster_save_config(rc.get_primaries())
nodes = target_nodes.values()
else:
- raise TypeError("target_nodes type can be one of the "
- "followings: node_flag (PRIMARIES, "
- "REPLICAS, RANDOM, ALL_NODES),"
- "ClusterNode, list<ClusterNode>, or "
- "dict<any, ClusterNode>. The passed type is {0}".
- format(type(target_nodes)))
+ raise TypeError(
+ "target_nodes type can be one of the following: "
+ "node_flag (PRIMARIES, REPLICAS, RANDOM, ALL_NODES),"
+ "ClusterNode, list<ClusterNode>, or dict<any, ClusterNode>. "
+ f"The passed type is {type(target_nodes)}"
+ )
return nodes
def execute_command(self, *args, **kwargs):
@@ -824,8 +824,7 @@ class RedisCluster(ClusterCommands, object):
*args, **kwargs, nodes_flag=target_nodes)
if not target_nodes:
raise RedisClusterException(
- "No targets were found to execute"
- " {} command on".format(args))
+ f"No targets were found to execute {args} command on")
for node in target_nodes:
res[node.name] = self._execute_command(
node, *args, **kwargs)
@@ -868,9 +867,10 @@ class RedisCluster(ClusterCommands, object):
command in READ_COMMANDS)
moved = False
- log.debug("Executing command {0} on target node: {1} {2}".
- format(command, target_node.server_type,
- target_node.name))
+ log.debug(
+ f"Executing command {command} on target node: "
+ f"{target_node.server_type} {target_node.name}"
+ )
redis_node = self.get_redis_connection(target_node)
connection = get_connection(redis_node, *args, **kwargs)
if asking:
@@ -952,7 +952,7 @@ class RedisCluster(ClusterCommands, object):
raise e
except ResponseError as e:
message = e.__str__()
- log.exception("ResponseError: {0}".format(message))
+ log.exception(f"ResponseError: {message}")
raise e
except BaseException as e:
log.exception("BaseException")
@@ -995,7 +995,7 @@ class RedisCluster(ClusterCommands, object):
return res
-class ClusterNode(object):
+class ClusterNode:
def __init__(self, host, port, server_type=None, redis_connection=None):
if host == 'localhost':
host = socket.gethostbyname(host)
@@ -1007,13 +1007,13 @@ class ClusterNode(object):
self.redis_connection = redis_connection
def __repr__(self):
- return '[host={0},port={1},' \
- 'name={2},server_type={3},redis_connection={4}]' \
- .format(self.host,
- self.port,
- self.name,
- self.server_type,
- self.redis_connection)
+ return (
+ f'[host={self.host},'
+ f'port={self.port},'
+ f'name={self.name},'
+ f'server_type={self.server_type},'
+ f'redis_connection={self.redis_connection}]'
+ )
def __eq__(self, obj):
return isinstance(obj, ClusterNode) and obj.name == self.name
@@ -1135,9 +1135,8 @@ class NodesManager:
if self.slots_cache.get(slot) is None or \
len(self.slots_cache[slot]) == 0:
raise SlotNotCoveredError(
- 'Slot "{0}" not covered by the cluster. '
- '"require_full_coverage={1}"'.format(
- slot, self._require_full_coverage)
+ f'Slot "{slot}" not covered by the cluster. '
+ f'"require_full_coverage={self._require_full_coverage}"'
)
if read_from_replicas is True:
@@ -1196,7 +1195,7 @@ class NodesManager:
except Exception as e:
raise RedisClusterException(
'ERROR sending "config get cluster-require-full-coverage"'
- ' command to redis server: {0}, {1}'.format(node.name, e)
+ f' command to redis server: {node.name}, {e}'
)
# at least one node should have cluster-require-full-coverage yes
@@ -1269,7 +1268,7 @@ class NodesManager:
msg = e.__str__
log.exception('An exception occurred while trying to'
' initialize the cluster using the seed node'
- ' {0}:\n{1}'.format(startup_node.name, msg))
+ f' {startup_node.name}:\n{msg}')
continue
except ResponseError as e:
log.exception(
@@ -1283,15 +1282,13 @@ class NodesManager:
else:
raise RedisClusterException(
'ERROR sending "cluster slots" command to redis '
- 'server: {0}. error: {1}'.format(
- startup_node, message)
+ f'server: {startup_node}. error: {message}'
)
except Exception as e:
message = e.__str__()
raise RedisClusterException(
'ERROR sending "cluster slots" command to redis '
- 'server: {0}. error: {1}'.format(
- startup_node, message)
+ f'server: {startup_node}. error: {message}'
)
# CLUSTER SLOTS command results in the following output:
@@ -1342,17 +1339,16 @@ class NodesManager:
else:
# Validate that 2 nodes want to use the same slot cache
# setup
- if tmp_slots[i][0].name != target_node.name:
+ tmp_slot = tmp_slots[i][0]
+ if tmp_slot.name != target_node.name:
disagreements.append(
- '{0} vs {1} on slot: {2}'.format(
- tmp_slots[i][0].name, target_node.name, i)
+ f'{tmp_slot.name} vs {target_node.name} on slot: {i}'
)
if len(disagreements) > 5:
raise RedisClusterException(
- 'startup_nodes could not agree on a valid'
- ' slots cache: {0}'.format(
- ", ".join(disagreements))
+ f'startup_nodes could not agree on a valid '
+ f'slots cache: {", ".join(disagreements)}'
)
if not startup_nodes_reachable:
@@ -1370,9 +1366,8 @@ class NodesManager:
# Despite the requirement that the slots be covered, there
# isn't a full coverage
raise RedisClusterException(
- 'All slots are not covered after query all startup_nodes.'
- ' {0} of {1} covered...'.format(
- len(self.slots_cache), REDIS_CLUSTER_HASH_SLOTS)
+ f'All slots are not covered after query all startup_nodes. '
+ f'{len(self.slots_cache)} of {REDIS_CLUSTER_HASH_SLOTS} covered...'
)
elif not fully_covered and not self._require_full_coverage:
# The user set require_full_coverage to False.
@@ -1389,8 +1384,7 @@ class NodesManager:
'cluster-require-full-coverage configuration to no on '
'all of the cluster nodes if you wish the cluster to '
'be able to serve without being fully covered.'
- ' {0} of {1} covered...'.format(
- len(self.slots_cache), REDIS_CLUSTER_HASH_SLOTS)
+ f'{len(self.slots_cache)} of {REDIS_CLUSTER_HASH_SLOTS} covered...'
)
# Set the tmp variables to the real variables
@@ -1495,8 +1489,7 @@ class ClusterPubSub(PubSub):
"""
if node is None or redis_cluster.get_node(node_name=node.name) is None:
raise RedisClusterException(
- "Node {0}:{1} doesn't exist in the cluster"
- .format(host, port))
+ f"Node {host}:{port} doesn't exist in the cluster")
def execute_command(self, *args, **kwargs):
"""
@@ -1585,7 +1578,7 @@ class ClusterPipeline(RedisCluster):
def __repr__(self):
"""
"""
- return "{0}".format(type(self).__name__)
+ return f"{type(self).__name__}"
def __enter__(self):
"""
@@ -1645,8 +1638,10 @@ class ClusterPipeline(RedisCluster):
Provides extra context to the exception prior to it being handled
"""
cmd = ' '.join(map(safe_str, command))
- msg = 'Command # %d (%s) of pipeline caused error: %s' % (
- number, cmd, exception.args[0])
+ msg = (
+ f'Command # {number} ({cmd}) of pipeline '
+ f'caused error: {exception.args[0]}'
+ )
exception.args = (msg,) + exception.args[1:]
def execute(self, raise_on_error=True):
@@ -1813,8 +1808,8 @@ class ClusterPipeline(RedisCluster):
# if we have more commands to attempt, we've run into problems.
# collect all the commands we are allowed to retry.
# (MOVED, ASK, or connection errors or timeout errors)
- attempt = sorted([c for c in attempt
- if isinstance(c.result, ERRORS_ALLOW_RETRY)],
+ attempt = sorted((c for c in attempt
+ if isinstance(c.result, ERRORS_ALLOW_RETRY)),
key=lambda x: x.position)
if attempt and allow_redirections:
# RETRY MAGIC HAPPENS HERE!
@@ -1835,12 +1830,12 @@ class ClusterPipeline(RedisCluster):
# If a lot of commands have failed, we'll be setting the
# flag to rebuild the slots table from scratch.
# So MOVED errors should correct themselves fairly quickly.
- msg = 'An exception occurred during pipeline execution. ' \
- 'args: {0}, error: {1} {2}'.\
- format(attempt[-1].args,
- type(attempt[-1].result).__name__,
- str(attempt[-1].result))
- log.exception(msg)
+ log.exception(
+ f'An exception occurred during pipeline execution. '
+ f'args: {attempt[-1].args}, '
+ f'error: {type(attempt[-1].result).__name__} '
+ f'{str(attempt[-1].result)}'
+ )
self.reinitialize_counter += 1
if self._should_reinitialized():
self.nodes_manager.initialize()
@@ -1848,8 +1843,7 @@ class ClusterPipeline(RedisCluster):
try:
# send each command individually like we
# do in the main client.
- c.result = super(ClusterPipeline, self). \
- execute_command(*c.args, **c.options)
+ c.result = super().execute_command(*c.args, **c.options)
except RedisError as e:
c.result = e
@@ -1933,8 +1927,8 @@ def block_pipeline_command(func):
def inner(*args, **kwargs):
raise RedisClusterException(
- "ERROR: Calling pipelined function {0} is blocked when "
- "running redis in cluster mode...".format(func.__name__))
+ f"ERROR: Calling pipelined function {func.__name__} is blocked when "
+ f"running redis in cluster mode...")
return inner
@@ -1977,7 +1971,7 @@ ClusterPipeline.readwrite = block_pipeline_command(RedisCluster.readwrite)
ClusterPipeline.readonly = block_pipeline_command(RedisCluster.readonly)
-class PipelineCommand(object):
+class PipelineCommand:
"""
"""
@@ -1992,7 +1986,7 @@ class PipelineCommand(object):
self.asking = False
-class NodeCommands(object):
+class NodeCommands:
"""
"""
diff --git a/redis/commands/cluster.py b/redis/commands/cluster.py
index 6c7740d..e6b0a08 100644
--- a/redis/commands/cluster.py
+++ b/redis/commands/cluster.py
@@ -228,8 +228,7 @@ class ClusterManagementCommands:
if _type is not None:
client_types = ('normal', 'master', 'slave', 'pubsub')
if str(_type).lower() not in client_types:
- raise DataError("CLIENT KILL type must be one of %r" % (
- client_types,))
+ raise DataError(f"CLIENT KILL type must be one of {client_types!r}")
args.extend((b'TYPE', _type))
if skipme is not None:
if not isinstance(skipme, bool):
@@ -267,8 +266,7 @@ class ClusterManagementCommands:
if _type is not None:
client_types = ('normal', 'master', 'replica', 'pubsub')
if str(_type).lower() not in client_types:
- raise DataError("CLIENT LIST _type must be one of %r" % (
- client_types,))
+ raise DataError(f"CLIENT LIST _type must be one of {client_types!r}")
return self.execute_command('CLIENT LIST',
b'TYPE',
_type,
@@ -302,7 +300,7 @@ class ClusterManagementCommands:
"""
replies = ['ON', 'OFF', 'SKIP']
if reply not in replies:
- raise DataError('CLIENT REPLY must be one of %r' % replies)
+ raise DataError(f'CLIENT REPLY must be one of {replies!r}')
return self.execute_command("CLIENT REPLY", reply,
target_nodes=target_nodes)
@@ -640,11 +638,10 @@ class ClusterManagementCommands:
# check validity
supported_algo = ['LCS']
if algo not in supported_algo:
- raise DataError("The supported algorithms are: %s"
- % (', '.join(supported_algo)))
+ supported_algos_str = ', '.join(supported_algo)
+ raise DataError(f"The supported algorithms are: {supported_algos_str}")
if specific_argument not in ['keys', 'strings']:
- raise DataError("specific_argument can be only"
- " keys or strings")
+ raise DataError("specific_argument can be only keys or strings")
if len and idx:
raise DataError("len and idx cannot be provided together.")
@@ -788,8 +785,7 @@ class ClusterCommands(ClusterManagementCommands, ClusterMultiKeyCommands,
if option:
if option.upper() not in ['FORCE', 'TAKEOVER']:
raise RedisError(
- 'Invalid option for CLUSTER FAILOVER command: {0}'.format(
- option))
+ f'Invalid option for CLUSTER FAILOVER command: {option}')
else:
return self.execute_command('CLUSTER FAILOVER', option,
target_nodes=target_node)
@@ -880,7 +876,7 @@ class ClusterCommands(ClusterManagementCommands, ClusterMultiKeyCommands,
raise RedisError('For "stable" state please use '
'cluster_setslot_stable')
else:
- raise RedisError('Invalid slot state: {0}'.format(state))
+ raise RedisError(f'Invalid slot state: {state}')
def cluster_setslot_stable(self, slot_id):
"""
diff --git a/redis/commands/core.py b/redis/commands/core.py
index 64e3b6d..0285f80 100644
--- a/redis/commands/core.py
+++ b/redis/commands/core.py
@@ -227,8 +227,8 @@ class ACLCommands:
elif password.startswith(b'-'):
pieces.append(b'<%s' % password[1:])
else:
- raise DataError('Password %d must be prefixeed with a '
- '"+" to add or a "-" to remove' % i)
+ raise DataError(f'Password {i} must be prefixed with a '
+ f'"+" to add or a "-" to remove')
if hashed_passwords:
# as most users will have only one password, allow remove_passwords
@@ -241,8 +241,8 @@ class ACLCommands:
elif hashed_password.startswith(b'-'):
pieces.append(b'!%s' % hashed_password[1:])
else:
- raise DataError('Hashed %d password must be prefixeed '
- 'with a "+" to add or a "-" to remove' % i)
+ raise DataError(f'Hashed password {i} must be prefixed with a '
+ f'"+" to add or a "-" to remove')
if nopass:
pieces.append(b'nopass')
@@ -260,16 +260,18 @@ class ACLCommands:
elif category.startswith(b'-'):
pieces.append(b'-@%s' % category[1:])
else:
- raise DataError('Category "%s" must be prefixed with '
- '"+" or "-"'
- % encoder.decode(category, force=True))
+ raise DataError(
+ f'Category "{encoder.decode(category, force=True)}" '
+ 'must be prefixed with "+" or "-"'
+ )
if commands:
for cmd in commands:
cmd = encoder.encode(cmd)
if not cmd.startswith(b'+') and not cmd.startswith(b'-'):
- raise DataError('Command "%s" must be prefixed with '
- '"+" or "-"'
- % encoder.decode(cmd, force=True))
+ raise DataError(
+ f'Command "{encoder.decode(cmd, force=True)}" '
+ 'must be prefixed with "+" or "-"'
+ )
pieces.append(cmd)
if keys:
@@ -342,8 +344,7 @@ class ManagementCommands:
if _type is not None:
client_types = ('normal', 'master', 'slave', 'pubsub')
if str(_type).lower() not in client_types:
- raise DataError("CLIENT KILL type must be one of %r" % (
- client_types,))
+ raise DataError(f"CLIENT KILL type must be one of {client_types!r}")
args.extend((b'TYPE', _type))
if skipme is not None:
if not isinstance(skipme, bool):
@@ -388,8 +389,7 @@ class ManagementCommands:
if _type is not None:
client_types = ('normal', 'master', 'replica', 'pubsub')
if str(_type).lower() not in client_types:
- raise DataError("CLIENT LIST _type must be one of %r" % (
- client_types,))
+ raise DataError(f"CLIENT LIST _type must be one of {client_types!r}")
args.append(b'TYPE')
args.append(_type)
if not isinstance(client_id, list):
@@ -434,7 +434,7 @@ class ManagementCommands:
"""
replies = ['ON', 'OFF', 'SKIP']
if reply not in replies:
- raise DataError('CLIENT REPLY must be one of %r' % replies)
+ raise DataError(f'CLIENT REPLY must be one of {replies!r}')
return self.execute_command("CLIENT REPLY", reply)
def client_id(self):
@@ -551,7 +551,7 @@ class ManagementCommands:
return self.execute_command('CONFIG REWRITE')
def cluster(self, cluster_arg, *args):
- return self.execute_command('CLUSTER %s' % cluster_arg.upper(), *args)
+ return self.execute_command(f'CLUSTER {cluster_arg.upper()}', *args)
def dbsize(self):
"""
@@ -1086,7 +1086,7 @@ class BasicKeyCommands:
For more information check https://redis.io/commands/getex
"""
- opset = set([ex, px, exat, pxat])
+ opset = {ex, px, exat, pxat}
if len(opset) > 2 or len(opset) > 1 and persist:
raise DataError("``ex``, ``px``, ``exat``, ``pxat``, "
"and ``persist`` are mutually exclusive.")
@@ -1554,11 +1554,10 @@ class BasicKeyCommands:
# check validity
supported_algo = ['LCS']
if algo not in supported_algo:
- raise DataError("The supported algorithms are: %s"
- % (', '.join(supported_algo)))
+ supported_algos_str = ', '.join(supported_algo)
+ raise DataError(f"The supported algorithms are: {supported_algos_str}")
if specific_argument not in ['keys', 'strings']:
- raise DataError("specific_argument can be only"
- " keys or strings")
+ raise DataError("specific_argument can be only keys or strings")
if len and idx:
raise DataError("len and idx cannot be provided together.")
@@ -3466,8 +3465,8 @@ class HashCommands:
For more information check https://redis.io/commands/hmset
"""
warnings.warn(
- '%s.hmset() is deprecated. Use %s.hset() instead.'
- % (self.__class__.__name__, self.__class__.__name__),
+ f'{self.__class__.__name__}.hmset() is deprecated. '
+ f'Use {self.__class__.__name__}.hset() instead.',
DeprecationWarning,
stacklevel=2,
)
diff --git a/redis/commands/helpers.py b/redis/commands/helpers.py
index 5e8ff49..dc5705b 100644
--- a/redis/commands/helpers.py
+++ b/redis/commands/helpers.py
@@ -113,4 +113,4 @@ def quote_string(v):
v = v.replace('"', '\\"')
- return '"{}"'.format(v)
+ return f'"{v}"'
diff --git a/redis/commands/json/path.py b/redis/commands/json/path.py
index 6d87045..f0a413a 100644
--- a/redis/commands/json/path.py
+++ b/redis/commands/json/path.py
@@ -1,4 +1,4 @@
-class Path(object):
+class Path:
"""This class represents a path in a JSON value."""
strPath = ""
diff --git a/redis/commands/parser.py b/redis/commands/parser.py
index d8b0327..26b190c 100644
--- a/redis/commands/parser.py
+++ b/redis/commands/parser.py
@@ -46,8 +46,9 @@ class CommandsParser:
# version has changed, the commands may not be current
self.initialize(redis_conn)
if cmd_name not in self.commands:
- raise RedisError("{0} command doesn't exist in Redis "
- "commands".format(cmd_name.upper()))
+ raise RedisError(
+ f"{cmd_name.upper()} command doesn't exist in Redis commands"
+ )
command = self.commands.get(cmd_name)
if 'movablekeys' in command['flags']:
diff --git a/redis/commands/search/__init__.py b/redis/commands/search/__init__.py
index 8320ad4..a30cebe 100644
--- a/redis/commands/search/__init__.py
+++ b/redis/commands/search/__init__.py
@@ -7,7 +7,7 @@ class Search(SearchCommands):
It abstracts the API of the module and lets you just use the engine.
"""
- class BatchIndexer(object):
+ class BatchIndexer:
"""
A batch indexer allows you to automatically batch
document indexing in pipelines, flushing it every N documents.
diff --git a/redis/commands/search/aggregation.py b/redis/commands/search/aggregation.py
index 3d71329..b1ac6b0 100644
--- a/redis/commands/search/aggregation.py
+++ b/redis/commands/search/aggregation.py
@@ -1,7 +1,7 @@
FIELDNAME = object()
-class Limit(object):
+class Limit:
def __init__(self, offset=0, count=0):
self.offset = offset
self.count = count
@@ -13,7 +13,7 @@ class Limit(object):
return []
-class Reducer(object):
+class Reducer:
"""
Base reducer object for all reducers.
@@ -55,7 +55,7 @@ class Reducer(object):
return self._args
-class SortDirection(object):
+class SortDirection:
"""
This special class is used to indicate sort direction.
"""
@@ -82,7 +82,7 @@ class Desc(SortDirection):
DIRSTRING = "DESC"
-class Group(object):
+class Group:
"""
This object automatically created in the `AggregateRequest.group_by()`
"""
@@ -109,7 +109,7 @@ class Group(object):
return ret
-class Projection(object):
+class Projection:
"""
This object automatically created in the `AggregateRequest.apply()`
"""
@@ -126,7 +126,7 @@ class Projection(object):
return ret
-class SortBy(object):
+class SortBy:
"""
This object automatically created in the `AggregateRequest.sort_by()`
"""
@@ -151,7 +151,7 @@ class SortBy(object):
return ret
-class AggregateRequest(object):
+class AggregateRequest:
"""
Aggregation request which can be passed to `Client.aggregate`.
"""
@@ -370,7 +370,7 @@ class AggregateRequest(object):
return ret
-class Cursor(object):
+class Cursor:
def __init__(self, cid):
self.cid = cid
self.max_idle = 0
@@ -385,16 +385,15 @@ class Cursor(object):
return args
-class AggregateResult(object):
+class AggregateResult:
def __init__(self, rows, cursor, schema):
self.rows = rows
self.cursor = cursor
self.schema = schema
def __repr__(self):
- return "<{} at 0x{:x} Rows={}, Cursor={}>".format(
- self.__class__.__name__,
- id(self),
- len(self.rows),
- self.cursor.cid if self.cursor else -1,
+ cid = self.cursor.cid if self.cursor else -1
+ return (
+ f"<{self.__class__.__name__} at 0x{id(self):x} "
+ f"Rows={len(self.rows)}, Cursor={cid}>"
)
diff --git a/redis/commands/search/commands.py b/redis/commands/search/commands.py
index ed58255..c19cb93 100644
--- a/redis/commands/search/commands.py
+++ b/redis/commands/search/commands.py
@@ -348,7 +348,7 @@ class SearchCommands:
# convert the query from a text to a query object
query = Query(query)
if not isinstance(query, Query):
- raise ValueError("Bad query type %s" % type(query))
+ raise ValueError(f"Bad query type {type(query)}")
args += query.get_args()
return args, query
diff --git a/redis/commands/search/document.py b/redis/commands/search/document.py
index 0d4255d..5b30505 100644
--- a/redis/commands/search/document.py
+++ b/redis/commands/search/document.py
@@ -1,4 +1,4 @@
-class Document(object):
+class Document:
"""
Represents a single document in a result set
"""
@@ -10,4 +10,4 @@ class Document(object):
setattr(self, k, v)
def __repr__(self):
- return "Document %s" % self.__dict__
+ return f"Document {self.__dict__}"
diff --git a/redis/commands/search/field.py b/redis/commands/search/field.py
index 45114a4..076c872 100644
--- a/redis/commands/search/field.py
+++ b/redis/commands/search/field.py
@@ -1,4 +1,4 @@
-class Field(object):
+class Field:
NUMERIC = "NUMERIC"
TEXT = "TEXT"
diff --git a/redis/commands/search/indexDefinition.py b/redis/commands/search/indexDefinition.py
index 4fbc609..0c7a3b0 100644
--- a/redis/commands/search/indexDefinition.py
+++ b/redis/commands/search/indexDefinition.py
@@ -8,7 +8,7 @@ class IndexType(Enum):
JSON = 2
-class IndexDefinition(object):
+class IndexDefinition:
"""IndexDefinition is used to define a index definition for automatic
indexing on Hash or Json update."""
@@ -38,8 +38,7 @@ class IndexDefinition(object):
elif index_type is IndexType.JSON:
self.args.extend(["ON", "JSON"])
elif index_type is not None:
- raise RuntimeError("index_type must be one of {}".
- format(list(IndexType)))
+ raise RuntimeError(f"index_type must be one of {list(IndexType)}")
def _appendPrefix(self, prefix):
"""Append PREFIX."""
diff --git a/redis/commands/search/query.py b/redis/commands/search/query.py
index 85a8255..5534f7b 100644
--- a/redis/commands/search/query.py
+++ b/redis/commands/search/query.py
@@ -1,4 +1,4 @@
-class Query(object):
+class Query:
"""
Query is used to build complex queries that have more parameters than just
the query string. The query string is set in the constructor, and other
@@ -291,7 +291,7 @@ class Query(object):
return self
-class Filter(object):
+class Filter:
def __init__(self, keyword, field, *args):
self.args = [keyword, field] + list(args)
@@ -303,8 +303,8 @@ class NumericFilter(Filter):
def __init__(self, field, minval, maxval, minExclusive=False,
maxExclusive=False):
args = [
- minval if not minExclusive else "({}".format(minval),
- maxval if not maxExclusive else "({}".format(maxval),
+ minval if not minExclusive else f"({minval}",
+ maxval if not maxExclusive else f"({maxval}",
]
Filter.__init__(self, "FILTER", field, *args)
@@ -320,6 +320,6 @@ class GeoFilter(Filter):
Filter.__init__(self, "GEOFILTER", field, lon, lat, radius, unit)
-class SortbyField(object):
+class SortbyField:
def __init__(self, field, asc=True):
self.args = [field, "ASC" if asc else "DESC"]
diff --git a/redis/commands/search/querystring.py b/redis/commands/search/querystring.py
index aecd3b8..ffba542 100644
--- a/redis/commands/search/querystring.py
+++ b/redis/commands/search/querystring.py
@@ -61,7 +61,7 @@ def geo(lat, lon, radius, unit="km"):
return GeoValue(lat, lon, radius, unit)
-class Value(object):
+class Value:
@property
def combinable(self):
"""
@@ -134,7 +134,7 @@ class GeoValue(Value):
self.unit = unit
-class Node(object):
+class Node:
def __init__(self, *children, **kwparams):
"""
Create a node
@@ -197,13 +197,11 @@ class Node(object):
def join_fields(self, key, vals):
if len(vals) == 1:
- return [BaseNode("@{}:{}".format(key, vals[0].to_string()))]
+ return [BaseNode(f"@{key}:{vals[0].to_string()}")]
if not vals[0].combinable:
- return [BaseNode("@{}:{}".format(key,
- v.to_string())) for v in vals]
+ return [BaseNode(f"@{key}:{v.to_string()}") for v in vals]
s = BaseNode(
- "@{}:({})".format(key,
- self.JOINSTR.join(v.to_string() for v in vals))
+ f"@{key}:({self.JOINSTR.join(v.to_string() for v in vals)})"
)
return [s]
@@ -220,9 +218,7 @@ class Node(object):
def to_string(self, with_parens=None):
with_parens = self._should_use_paren(with_parens)
pre, post = ("(", ")") if with_parens else ("", "")
- return "{}{}{}".format(
- pre, self.JOINSTR.join(n.to_string() for n in self.params), post
- )
+ return f"{pre}{self.JOINSTR.join(n.to_string() for n in self.params)}{post}"
def _should_use_paren(self, optval):
if optval is not None:
@@ -235,7 +231,7 @@ class Node(object):
class BaseNode(Node):
def __init__(self, s):
- super(BaseNode, self).__init__()
+ super().__init__()
self.s = str(s)
def to_string(self, with_parens=None):
@@ -268,7 +264,7 @@ class DisjunctNode(IntersectNode):
def to_string(self, with_parens=None):
with_parens = self._should_use_paren(with_parens)
- ret = super(DisjunctNode, self).to_string(with_parens=False)
+ ret = super().to_string(with_parens=False)
if with_parens:
return "(-" + ret + ")"
else:
@@ -294,7 +290,7 @@ class OptionalNode(IntersectNode):
def to_string(self, with_parens=None):
with_parens = self._should_use_paren(with_parens)
- ret = super(OptionalNode, self).to_string(with_parens=False)
+ ret = super().to_string(with_parens=False)
if with_parens:
return "(~" + ret + ")"
else:
diff --git a/redis/commands/search/reducers.py b/redis/commands/search/reducers.py
index 6cbbf2f..41ed11a 100644
--- a/redis/commands/search/reducers.py
+++ b/redis/commands/search/reducers.py
@@ -3,7 +3,7 @@ from .aggregation import Reducer, SortDirection
class FieldOnlyReducer(Reducer):
def __init__(self, field):
- super(FieldOnlyReducer, self).__init__(field)
+ super().__init__(field)
self._field = field
@@ -15,7 +15,7 @@ class count(Reducer):
NAME = "COUNT"
def __init__(self):
- super(count, self).__init__()
+ super().__init__()
class sum(FieldOnlyReducer):
@@ -26,7 +26,7 @@ class sum(FieldOnlyReducer):
NAME = "SUM"
def __init__(self, field):
- super(sum, self).__init__(field)
+ super().__init__(field)
class min(FieldOnlyReducer):
@@ -37,7 +37,7 @@ class min(FieldOnlyReducer):
NAME = "MIN"
def __init__(self, field):
- super(min, self).__init__(field)
+ super().__init__(field)
class max(FieldOnlyReducer):
@@ -48,7 +48,7 @@ class max(FieldOnlyReducer):
NAME = "MAX"
def __init__(self, field):
- super(max, self).__init__(field)
+ super().__init__(field)
class avg(FieldOnlyReducer):
@@ -59,7 +59,7 @@ class avg(FieldOnlyReducer):
NAME = "AVG"
def __init__(self, field):
- super(avg, self).__init__(field)
+ super().__init__(field)
class tolist(FieldOnlyReducer):
@@ -70,7 +70,7 @@ class tolist(FieldOnlyReducer):
NAME = "TOLIST"
def __init__(self, field):
- super(tolist, self).__init__(field)
+ super().__init__(field)
class count_distinct(FieldOnlyReducer):
@@ -82,7 +82,7 @@ class count_distinct(FieldOnlyReducer):
NAME = "COUNT_DISTINCT"
def __init__(self, field):
- super(count_distinct, self).__init__(field)
+ super().__init__(field)
class count_distinctish(FieldOnlyReducer):
@@ -104,7 +104,7 @@ class quantile(Reducer):
NAME = "QUANTILE"
def __init__(self, field, pct):
- super(quantile, self).__init__(field, str(pct))
+ super().__init__(field, str(pct))
self._field = field
@@ -116,7 +116,7 @@ class stddev(FieldOnlyReducer):
NAME = "STDDEV"
def __init__(self, field):
- super(stddev, self).__init__(field)
+ super().__init__(field)
class first_value(Reducer):
@@ -155,7 +155,7 @@ class first_value(Reducer):
args = [field]
if fieldstrs:
args += ["BY"] + fieldstrs
- super(first_value, self).__init__(*args)
+ super().__init__(*args)
self._field = field
@@ -174,5 +174,5 @@ class random_sample(Reducer):
**size**: Return this many items (can be less)
"""
args = [field, str(size)]
- super(random_sample, self).__init__(*args)
+ super().__init__(*args)
self._field = field
diff --git a/redis/commands/search/result.py b/redis/commands/search/result.py
index 9cd922a..57ba53d 100644
--- a/redis/commands/search/result.py
+++ b/redis/commands/search/result.py
@@ -2,7 +2,7 @@ from .document import Document
from ._util import to_string
-class Result(object):
+class Result:
"""
Represents the result of a search query, and has an array of Document
objects
@@ -70,4 +70,4 @@ class Result(object):
self.docs.append(doc)
def __repr__(self):
- return "Result{%d total, docs: %s}" % (self.total, self.docs)
+ return f"Result{{{self.total} total, docs: {self.docs}}}"
diff --git a/redis/commands/search/suggestion.py b/redis/commands/search/suggestion.py
index 3401af9..6d295a6 100644
--- a/redis/commands/search/suggestion.py
+++ b/redis/commands/search/suggestion.py
@@ -1,7 +1,7 @@
from ._util import to_string
-class Suggestion(object):
+class Suggestion:
"""
Represents a single suggestion being sent or returned from the
autocomplete server
@@ -16,7 +16,7 @@ class Suggestion(object):
return self.string
-class SuggestionParser(object):
+class SuggestionParser:
"""
Internal class used to parse results from the `SUGGET` command.
This needs to consume either 1, 2, or 3 values at a time from
diff --git a/redis/commands/timeseries/info.py b/redis/commands/timeseries/info.py
index 3b89503..2b8acd1 100644
--- a/redis/commands/timeseries/info.py
+++ b/redis/commands/timeseries/info.py
@@ -2,7 +2,7 @@ from .utils import list_to_dict
from ..helpers import nativestr
-class TSInfo(object):
+class TSInfo:
"""
Hold information and statistics on the time-series.
Can be created using ``tsinfo`` command
diff --git a/redis/connection.py b/redis/connection.py
index 6ff3650..ef3a667 100755
--- a/redis/connection.py
+++ b/redis/connection.py
@@ -107,8 +107,8 @@ class Encoder:
elif not isinstance(value, str):
# a value we don't know how to deal with. throw an error
typename = type(value).__name__
- raise DataError("Invalid input of type: '%s'. Convert to a "
- "bytes, string, int or float first." % typename)
+ raise DataError(f"Invalid input of type: '{typename}'. "
+ f"Convert to a bytes, string, int or float first.")
if isinstance(value, str):
value = value.encode(self.encoding, self.encoding_errors)
return value
@@ -214,8 +214,7 @@ class SocketBuffer:
allowed = NONBLOCKING_EXCEPTION_ERROR_NUMBERS.get(ex.__class__, -1)
if not raise_on_timeout and ex.errno == allowed:
return False
- raise ConnectionError("Error while reading from socket: %s" %
- (ex.args,))
+ raise ConnectionError(f"Error while reading from socket: {ex.args}")
finally:
if custom_timeout:
sock.settimeout(self.socket_timeout)
@@ -323,7 +322,7 @@ class PythonParser(BaseParser):
byte, response = raw[:1], raw[1:]
if byte not in (b'-', b'+', b':', b'$', b'*'):
- raise InvalidResponse("Protocol Error: %r" % raw)
+ raise InvalidResponse(f"Protocol Error: {raw!r}")
# server returned an error
if byte == b'-':
@@ -445,8 +444,7 @@ class HiredisParser(BaseParser):
allowed = NONBLOCKING_EXCEPTION_ERROR_NUMBERS.get(ex.__class__, -1)
if not raise_on_timeout and ex.errno == allowed:
return False
- raise ConnectionError("Error while reading from socket: %s" %
- (ex.args,))
+ raise ConnectionError(f"Error while reading from socket: {ex.args}")
finally:
if custom_timeout:
sock.settimeout(self._socket_timeout)
@@ -538,8 +536,8 @@ class Connection:
self._buffer_cutoff = 6000
def __repr__(self):
- repr_args = ','.join(['%s=%s' % (k, v) for k, v in self.repr_pieces()])
- return '%s<%s>' % (self.__class__.__name__, repr_args)
+ repr_args = ','.join([f'{k}={v}' for k, v in self.repr_pieces()])
+ return f'{self.__class__.__name__}<{repr_args}>'
def repr_pieces(self):
pieces = [
@@ -579,7 +577,7 @@ class Connection:
sock = self._connect()
except socket.timeout:
raise TimeoutError("Timeout connecting to server")
- except socket.error as e:
+ except OSError as e:
raise ConnectionError(self._error_message(e))
self._sock = sock
@@ -646,11 +644,12 @@ class Connection:
# args for socket.error can either be (errno, "message")
# or just "message"
if len(exception.args) == 1:
- return "Error connecting to %s:%s. %s." % \
- (self.host, self.port, exception.args[0])
+ return f"Error connecting to {self.host}:{self.port}. {exception.args[0]}."
else:
- return "Error %s connecting to %s:%s. %s." % \
- (exception.args[0], self.host, self.port, exception.args[1])
+ return (
+ f"Error {exception.args[0]} connecting to "
+ f"{self.host}:{self.port}. {exception.args[1]}."
+ )
def on_connect(self):
"Initialize the connection, authenticate and select a database"
@@ -734,15 +733,14 @@ class Connection:
except socket.timeout:
self.disconnect()
raise TimeoutError("Timeout writing to socket")
- except socket.error as e:
+ except OSError as e:
self.disconnect()
if len(e.args) == 1:
errno, errmsg = 'UNKNOWN', e.args[0]
else:
errno = e.args[0]
errmsg = e.args[1]
- raise ConnectionError("Error %s while writing to socket. %s." %
- (errno, errmsg))
+ raise ConnectionError(f"Error {errno} while writing to socket. {errmsg}.")
except BaseException:
self.disconnect()
raise
@@ -767,12 +765,12 @@ class Connection:
)
except socket.timeout:
self.disconnect()
- raise TimeoutError("Timeout reading from %s:%s" %
- (self.host, self.port))
- except socket.error as e:
+ raise TimeoutError(f"Timeout reading from {self.host}:{self.port}")
+ except OSError as e:
self.disconnect()
- raise ConnectionError("Error while reading from %s:%s : %s" %
- (self.host, self.port, e.args))
+ raise ConnectionError(
+ f"Error while reading from {self.host}:{self.port}"
+ f" : {e.args}")
except BaseException:
self.disconnect()
raise
@@ -867,8 +865,7 @@ class SSLConnection(Connection):
}
if ssl_cert_reqs not in CERT_REQS:
raise RedisError(
- "Invalid SSL Certificate Requirements Flag: %s" %
- ssl_cert_reqs)
+ f"Invalid SSL Certificate Requirements Flag: {ssl_cert_reqs}")
ssl_cert_reqs = CERT_REQS[ssl_cert_reqs]
self.cert_reqs = ssl_cert_reqs
self.ca_certs = ssl_ca_certs
@@ -947,11 +944,12 @@ class UnixDomainSocketConnection(Connection):
# args for socket.error can either be (errno, "message")
# or just "message"
if len(exception.args) == 1:
- return "Error connecting to unix socket: %s. %s." % \
- (self.path, exception.args[0])
+ return f"Error connecting to unix socket: {self.path}. {exception.args[0]}."
else:
- return "Error %s connecting to unix socket: %s. %s." % \
- (exception.args[0], self.path, exception.args[1])
+ return (
+ f"Error {exception.args[0]} connecting to unix socket: "
+ f"{self.path}. {exception.args[1]}."
+ )
FALSE_STRINGS = ('0', 'F', 'FALSE', 'N', 'NO')
@@ -990,7 +988,7 @@ def parse_url(url):
kwargs[name] = parser(value)
except (TypeError, ValueError):
raise ValueError(
- "Invalid value for `%s` in connection URL." % name
+ f"Invalid value for `{name}` in connection URL."
)
else:
kwargs[name] = value
@@ -1023,9 +1021,8 @@ def parse_url(url):
if url.scheme == 'rediss':
kwargs['connection_class'] = SSLConnection
else:
- valid_schemes = 'redis://, rediss://, unix://'
raise ValueError('Redis URL must specify one of the following '
- 'schemes (%s)' % valid_schemes)
+ 'schemes (redis://, rediss://, unix://)')
return kwargs
@@ -1109,9 +1106,9 @@ class ConnectionPool:
self.reset()
def __repr__(self):
- return "%s<%s>" % (
- type(self).__name__,
- repr(self.connection_class(**self.connection_kwargs)),
+ return (
+ f"{type(self).__name__}"
+ f"<{repr(self.connection_class(**self.connection_kwargs))}>"
)
def reset(self):
diff --git a/redis/sentinel.py b/redis/sentinel.py
index 3efd58f..06877bd 100644
--- a/redis/sentinel.py
+++ b/redis/sentinel.py
@@ -24,9 +24,9 @@ class SentinelManagedConnection(Connection):
def __repr__(self):
pool = self.connection_pool
- s = '%s<service=%s%%s>' % (type(self).__name__, pool.service_name)
+ s = f'{type(self).__name__}<service={pool.service_name}%s>'
if self.host:
- host_info = ',host=%s,port=%s' % (self.host, self.port)
+ host_info = f',host={self.host},port={self.port}'
s = s % host_info
return s
@@ -91,11 +91,8 @@ class SentinelConnectionPool(ConnectionPool):
self.sentinel_manager = sentinel_manager
def __repr__(self):
- return "%s<service=%s(%s)" % (
- type(self).__name__,
- self.service_name,
- self.is_master and 'master' or 'slave',
- )
+ role = 'master' if self.is_master else 'slave'
+ return f"{type(self).__name__}<service={self.service_name}({role})"
def reset(self):
super().reset()
@@ -106,7 +103,7 @@ class SentinelConnectionPool(ConnectionPool):
check = not self.is_master or \
(self.is_master and
self.master_address == (connection.host, connection.port))
- parent = super(SentinelConnectionPool, self)
+ parent = super()
return check and parent.owns_connection(connection)
def get_master_address(self):
@@ -136,10 +133,10 @@ class SentinelConnectionPool(ConnectionPool):
yield self.get_master_address()
except MasterNotFoundError:
pass
- raise SlaveNotFoundError('No slave found for %r' % (self.service_name))
+ raise SlaveNotFoundError(f'No slave found for {self.service_name!r}')
-class Sentinel(SentinelCommands, object):
+class Sentinel(SentinelCommands):
"""
Redis Sentinel cluster client
@@ -205,13 +202,10 @@ class Sentinel(SentinelCommands, object):
def __repr__(self):
sentinel_addresses = []
for sentinel in self.sentinels:
- sentinel_addresses.append('%s:%s' % (
- sentinel.connection_pool.connection_kwargs['host'],
- sentinel.connection_pool.connection_kwargs['port'],
+ sentinel_addresses.append('{host}:{port}'.format_map(
+ sentinel.connection_pool.connection_kwargs,
))
- return '%s<sentinels=[%s]>' % (
- type(self).__name__,
- ','.join(sentinel_addresses))
+ return f'{type(self).__name__}<sentinels=[{",".join(sentinel_addresses)}]>'
def check_master_state(self, state, service_name):
if not state['is_master'] or state['is_sdown'] or state['is_odown']:
@@ -240,7 +234,7 @@ class Sentinel(SentinelCommands, object):
self.sentinels[0], self.sentinels[sentinel_no] = (
sentinel, self.sentinels[0])
return state['ip'], state['port']
- raise MasterNotFoundError("No master found for %r" % (service_name,))
+ raise MasterNotFoundError(f"No master found for {service_name!r}")
def filter_slaves(self, slaves):
"Remove slaves that are in an ODOWN or SDOWN state"
diff --git a/tasks.py b/tasks.py
index e482194..8d9c4c6 100644
--- a/tasks.py
+++ b/tasks.py
@@ -16,7 +16,7 @@ def devenv(c):
clean(c)
cmd = 'tox -e devenv'
for d in dockers:
- cmd += " --docker-dont-stop={}".format(d)
+ cmd += f" --docker-dont-stop={d}"
run(cmd)
@@ -73,7 +73,7 @@ def clean(c):
shutil.rmtree("build")
if os.path.isdir("dist"):
shutil.rmtree("dist")
- run("docker rm -f {}".format(' '.join(dockers)))
+ run(f"docker rm -f {' '.join(dockers)}")
@task
diff --git a/tests/conftest.py b/tests/conftest.py
index ddc0834..8ed39ab 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -87,8 +87,7 @@ def wait_for_cluster_creation(redis_url, cluster_nodes, timeout=20):
now = time.time()
end_time = now + timeout
client = None
- print("Waiting for {0} cluster nodes to become available".
- format(cluster_nodes))
+ print(f"Waiting for {cluster_nodes} cluster nodes to become available")
while now < end_time:
try:
client = redis.RedisCluster.from_url(redis_url)
@@ -102,9 +101,8 @@ def wait_for_cluster_creation(redis_url, cluster_nodes, timeout=20):
if now >= end_time:
available_nodes = 0 if client is None else len(client.get_nodes())
raise RedisClusterException(
- "The cluster did not become available after {0} seconds. "
- "Only {1} nodes out of {2} are available".format(
- timeout, available_nodes, cluster_nodes))
+ f"The cluster did not become available after {timeout} seconds. "
+ f"Only {available_nodes} nodes out of {cluster_nodes} are available")
def skip_if_server_version_lt(min_version):
@@ -112,7 +110,7 @@ def skip_if_server_version_lt(min_version):
check = LooseVersion(redis_version) < LooseVersion(min_version)
return pytest.mark.skipif(
check,
- reason="Redis version required >= {}".format(min_version))
+ reason=f"Redis version required >= {min_version}")
def skip_if_server_version_gte(min_version):
@@ -120,12 +118,12 @@ def skip_if_server_version_gte(min_version):
check = LooseVersion(redis_version) >= LooseVersion(min_version)
return pytest.mark.skipif(
check,
- reason="Redis version required < {}".format(min_version))
+ reason=f"Redis version required < {min_version}")
def skip_unless_arch_bits(arch_bits):
return pytest.mark.skipif(REDIS_INFO["arch_bits"] != arch_bits,
- reason="server is not {}-bit".format(arch_bits))
+ reason=f"server is not {arch_bits}-bit")
def skip_ifmodversion_lt(min_version: str, module_name: str):
@@ -144,7 +142,7 @@ def skip_ifmodversion_lt(min_version: str, module_name: str):
check = version < mv
return pytest.mark.skipif(check, reason="Redis module version")
- raise AttributeError("No redis module named {}".format(module_name))
+ raise AttributeError(f"No redis module named {module_name}")
def skip_if_redis_enterprise(func):
@@ -320,8 +318,8 @@ def wait_for_command(client, monitor, command):
if LooseVersion(redis_version) >= LooseVersion('5.0.0'):
id_str = str(client.client_id())
else:
- id_str = '%08x' % random.randrange(2**32)
- key = '__REDIS-PY-%s__' % id_str
+ id_str = f'{random.randrange(2 ** 32):08x}'
+ key = f'__REDIS-PY-{id_str}__'
client.get(key)
while True:
monitor_response = monitor.next_command()
diff --git a/tests/test_cluster.py b/tests/test_cluster.py
index 071cb7d..d12e47e 100644
--- a/tests/test_cluster.py
+++ b/tests/test_cluster.py
@@ -178,7 +178,7 @@ def moved_redirection_helper(request, failover=False):
return "MOCK_OK"
parse_response.side_effect = ok_response
- raise MovedError("{0} {1}:{2}".format(slot, r_host, r_port))
+ raise MovedError(f"{slot} {r_host}:{r_port}")
parse_response.side_effect = moved_redirect_effect
assert rc.execute_command("SET", "foo", "bar") == "MOCK_OK"
@@ -229,7 +229,7 @@ class TestRedisClusterObj:
"cluster"), str_if_bytes(ex.value)
def test_from_url(self, r):
- redis_url = "redis://{0}:{1}/0".format(default_host, default_port)
+ redis_url = f"redis://{default_host}:{default_port}/0"
with patch.object(RedisCluster, 'from_url') as from_url:
def from_url_mocked(_url, **_kwargs):
return get_mocked_redis_client(url=_url, **_kwargs)
@@ -333,8 +333,7 @@ class TestRedisClusterObj:
return "MOCK_OK"
parse_response.side_effect = ok_response
- raise AskError("12182 {0}:{1}".format(redirect_node.host,
- redirect_node.port))
+ raise AskError(f"12182 {redirect_node.host}:{redirect_node.port}")
parse_response.side_effect = ask_redirect_effect
@@ -498,14 +497,14 @@ class TestRedisClusterObj:
assert r.keyslot(125) == r.keyslot(b"125")
assert r.keyslot(125) == r.keyslot("\x31\x32\x35")
assert r.keyslot("大奖") == r.keyslot(b"\xe5\xa4\xa7\xe5\xa5\x96")
- assert r.keyslot(u"大奖") == r.keyslot(b"\xe5\xa4\xa7\xe5\xa5\x96")
+ assert r.keyslot("大奖") == r.keyslot(b"\xe5\xa4\xa7\xe5\xa5\x96")
assert r.keyslot(1337.1234) == r.keyslot("1337.1234")
assert r.keyslot(1337) == r.keyslot("1337")
assert r.keyslot(b"abc") == r.keyslot("abc")
def test_get_node_name(self):
assert get_node_name(default_host, default_port) == \
- "{0}:{1}".format(default_host, default_port)
+ f"{default_host}:{default_port}"
def test_all_nodes(self, r):
"""
@@ -713,7 +712,7 @@ class TestClusterRedisCommands:
pubsub_nodes = []
i = 0
for node in nodes:
- channel = "foo{0}".format(i)
+ channel = f"foo{i}"
# We will create different pubsub clients where each one is
# connected to a different node
p = r.pubsub(node)
@@ -1180,8 +1179,7 @@ class TestClusterRedisCommands:
clients = [client for client in r.client_list(target_nodes=node)
if client.get('name') in ['redis-py-c1', 'redis-py-c2']]
assert len(clients) == 2
- clients_by_name = dict([(client.get('name'), client)
- for client in clients])
+ clients_by_name = {client.get('name'): client for client in clients}
client_addr = clients_by_name['redis-py-c2'].get('addr')
assert r.client_kill(client_addr, target_nodes=node) is True
@@ -2374,8 +2372,7 @@ class TestClusterPipeline:
warnings.warn("skipping this test since the cluster has only one "
"node")
return
- ask_msg = "{0} {1}:{2}".format(r.keyslot(key), ask_node.host,
- ask_node.port)
+ ask_msg = f"{r.keyslot(key)} {ask_node.host}:{ask_node.port}"
def raise_ask_error():
raise AskError(ask_msg)
@@ -2435,9 +2432,7 @@ class TestReadOnlyPipeline:
with r.pipeline() as readwrite_pipe:
mock_node_resp(primary, "MOCK_FOO")
if replica is not None:
- moved_error = "{0} {1}:{2}".format(r.keyslot(key),
- primary.host,
- primary.port)
+ moved_error = f"{r.keyslot(key)} {primary.host}:{primary.port}"
def raise_moved_error():
raise MovedError(moved_error)
diff --git a/tests/test_commands.py b/tests/test_commands.py
index f526ae5..444a163 100644
--- a/tests/test_commands.py
+++ b/tests/test_commands.py
@@ -35,7 +35,7 @@ def slowlog(request, r):
def redis_server_time(client):
seconds, milliseconds = client.time()
- timestamp = float('%s.%s' % (seconds, milliseconds))
+ timestamp = float(f'{seconds}.{milliseconds}')
return datetime.datetime.fromtimestamp(timestamp)
@@ -99,7 +99,7 @@ class TestRedisCommands:
assert r.acl_deluser(username) == 1
# now, a group of users
- users = ['bogususer_%d' % r for r in range(0, 5)]
+ users = [f'bogususer_{r}' for r in range(0, 5)]
for u in users:
r.acl_setuser(u, enabled=False, reset=True)
assert r.acl_deluser(*users) > 1
@@ -162,11 +162,11 @@ class TestRedisCommands:
commands=['+get', '+mget', '-hset'],
keys=['cache:*', 'objects:*'])
acl = r.acl_getuser(username)
- assert set(acl['categories']) == set(['-@all', '+@set', '+@hash'])
- assert set(acl['commands']) == set(['+get', '+mget', '-hset'])
+ assert set(acl['categories']) == {'-@all', '+@set', '+@hash'}
+ assert set(acl['commands']) == {'+get', '+mget', '-hset'}
assert acl['enabled'] is True
assert 'on' in acl['flags']
- assert set(acl['keys']) == set([b'cache:*', b'objects:*'])
+ assert set(acl['keys']) == {b'cache:*', b'objects:*'}
assert len(acl['passwords']) == 2
# test reset=False keeps existing ACL and applies new ACL on top
@@ -181,11 +181,11 @@ class TestRedisCommands:
commands=['+mget'],
keys=['objects:*'])
acl = r.acl_getuser(username)
- assert set(acl['categories']) == set(['-@all', '+@set', '+@hash'])
- assert set(acl['commands']) == set(['+get', '+mget'])
+ assert set(acl['categories']) == {'-@all', '+@set', '+@hash'}
+ assert set(acl['commands']) == {'+get', '+mget'}
assert acl['enabled'] is True
assert 'on' in acl['flags']
- assert set(acl['keys']) == set([b'cache:*', b'objects:*'])
+ assert set(acl['keys']) == {b'cache:*', b'objects:*'}
assert len(acl['passwords']) == 2
# test removal of passwords
@@ -405,8 +405,7 @@ class TestRedisCommands:
if client.get('name') in ['redis-py-c1', 'redis-py-c2']]
assert len(clients) == 2
- clients_by_name = dict([(client.get('name'), client)
- for client in clients])
+ clients_by_name = {client.get('name'): client for client in clients}
client_addr = clients_by_name['redis-py-c2'].get('addr')
assert r.client_kill(client_addr) is True
@@ -439,8 +438,7 @@ class TestRedisCommands:
if client.get('name') in ['redis-py-c1', 'redis-py-c2']]
assert len(clients) == 2
- clients_by_name = dict([(client.get('name'), client)
- for client in clients])
+ clients_by_name = {client.get('name'): client for client in clients}
client_2_id = clients_by_name['redis-py-c2'].get('id')
resp = r.client_kill_filter(_id=client_2_id)
@@ -460,8 +458,7 @@ class TestRedisCommands:
if client.get('name') in ['redis-py-c1', 'redis-py-c2']]
assert len(clients) == 2
- clients_by_name = dict([(client.get('name'), client)
- for client in clients])
+ clients_by_name = {client.get('name'): client for client in clients}
client_2_addr = clients_by_name['redis-py-c2'].get('addr')
resp = r.client_kill_filter(addr=client_2_addr)
@@ -487,8 +484,7 @@ class TestRedisCommands:
if client.get('name') in ['redis-py-c1', 'redis-py-c2']]
assert len(clients) == 2
- clients_by_name = dict([(client.get('name'), client)
- for client in clients])
+ clients_by_name = {client.get('name'): client for client in clients}
client_2_addr = clients_by_name['redis-py-c2'].get('laddr')
assert r.client_kill_filter(laddr=client_2_addr)
@@ -1809,11 +1805,11 @@ class TestRedisCommands:
def test_zadd_gt_lt(self, r):
for i in range(1, 20):
- r.zadd('a', {'a%s' % i: i})
+ r.zadd('a', {f'a{i}': i})
assert r.zadd('a', {'a20': 5}, gt=3) == 1
for i in range(1, 20):
- r.zadd('a', {'a%s' % i: i})
+ r.zadd('a', {f'a{i}': i})
assert r.zadd('a', {'a2': 5}, lt=1) == 0
# cannot use both nx and xx options
diff --git a/tests/test_connection.py b/tests/test_connection.py
index cd8907d..0071aca 100644
--- a/tests/test_connection.py
+++ b/tests/test_connection.py
@@ -15,7 +15,7 @@ def test_invalid_response(r):
with mock.patch.object(parser._buffer, 'readline', return_value=raw):
with pytest.raises(InvalidResponse) as cm:
parser.read_response()
- assert str(cm.value) == 'Protocol Error: %r' % raw
+ assert str(cm.value) == f'Protocol Error: {raw!r}'
@skip_if_server_version_lt('4.0.0')
diff --git a/tests/test_multiprocessing.py b/tests/test_multiprocessing.py
index d0feef1..5968b2b 100644
--- a/tests/test_multiprocessing.py
+++ b/tests/test_multiprocessing.py
@@ -89,9 +89,7 @@ class TestMultiprocessing:
A child will create its own connections when using a pool created
by a parent.
"""
- pool = ConnectionPool.from_url('redis://{}:{}'.format(master_host[0],
- master_host[1],
- ),
+ pool = ConnectionPool.from_url(f'redis://{master_host[0]}:{master_host[1]}',
max_connections=max_connections)
conn = pool.get_connection('ping')
@@ -126,8 +124,7 @@ class TestMultiprocessing:
A child process that uses the same pool as its parent isn't affected
when the parent disconnects all connections within the pool.
"""
- pool = ConnectionPool.from_url('redis://{}:{}'.format(master_host[0],
- master_host[1]),
+ pool = ConnectionPool.from_url(f'redis://{master_host[0]}:{master_host[1]}',
max_connections=max_connections)
conn = pool.get_connection('ping')
diff --git a/tests/test_pipeline.py b/tests/test_pipeline.py
index a759bc9..a87ed71 100644
--- a/tests/test_pipeline.py
+++ b/tests/test_pipeline.py
@@ -345,7 +345,7 @@ class TestPipeline:
with pytest.raises(redis.ResponseError) as ex:
pipe.execute()
- expected = 'Command # 1 (LLEN %s) of pipeline caused error: ' % key
+ expected = f'Command # 1 (LLEN {key}) of pipeline caused error: '
assert str(ex.value).startswith(expected)
assert r[key] == b'1'
diff --git a/tests/test_pubsub.py b/tests/test_pubsub.py
index 95513a0..b019bae 100644
--- a/tests/test_pubsub.py
+++ b/tests/test_pubsub.py
@@ -55,7 +55,7 @@ def make_subscribe_test_data(pubsub, type):
'unsub_func': pubsub.punsubscribe,
'keys': ['f*', 'b*', 'uni' + chr(4456) + '*']
}
- assert False, 'invalid subscribe type: %s' % type
+ assert False, f'invalid subscribe type: {type}'
class TestPubSubSubscribeUnsubscribe:
diff --git a/tests/test_search.py b/tests/test_search.py
index b65ac8d..c7b570c 100644
--- a/tests/test_search.py
+++ b/tests/test_search.py
@@ -99,7 +99,7 @@ def createIndex(client, num_docs=100, definition=None):
play, chapter, _, text = \
line[1], line[2], line[4], line[5]
- key = "{}:{}".format(play, chapter).lower()
+ key = f"{play}:{chapter}".lower()
d = chapters.setdefault(key, {})
d["play"] = play
d["txt"] = d.get("txt", "") + " " + text
@@ -861,7 +861,7 @@ def test_phonetic_matcher(client):
res = client.ft().search(Query("Jon"))
assert 2 == len(res.docs)
- assert ["John", "Jon"] == sorted([d.name for d in res.docs])
+ assert ["John", "Jon"] == sorted(d.name for d in res.docs)
@pytest.mark.redismod
diff --git a/tests/test_timeseries.py b/tests/test_timeseries.py
index c0fb09e..0743357 100644
--- a/tests/test_timeseries.py
+++ b/tests/test_timeseries.py
@@ -31,7 +31,7 @@ def test_create(client):
def test_create_duplicate_policy(client):
# Test for duplicate policy
for duplicate_policy in ["block", "last", "first", "min", "max"]:
- ts_name = "time-serie-ooo-{0}".format(duplicate_policy)
+ ts_name = f"time-serie-ooo-{duplicate_policy}"
assert client.ts().create(ts_name, duplicate_policy=duplicate_policy)
info = client.ts().info(ts_name)
assert duplicate_policy == info.duplicate_policy
diff --git a/tox.ini b/tox.ini
index dd68274..d06f7e3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -130,8 +130,9 @@ commands = /usr/bin/echo
deps_files = dev_requirements.txt
docker =
commands =
- flake8
+ flake8 --max-line-length=88
vulture redis whitelist.py --min-confidence 80
+ flynt --fail-on-change --dry-run .
skipsdist = true
skip_install = true