From 1d3fe0fb2d4c11a01ce3e98b4d4e624771937a8c Mon Sep 17 00:00:00 2001 From: Theo Despoudis Date: Tue, 4 Dec 2018 15:07:39 +0000 Subject: Add client kill with filter Signed-off-by: Theo Despoudis --- redis/client.py | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'redis/client.py') diff --git a/redis/client.py b/redis/client.py index 957081c..6791d67 100755 --- a/redis/client.py +++ b/redis/client.py @@ -471,7 +471,7 @@ class Redis(object): { 'CLIENT GETNAME': lambda r: r and nativestr(r), 'CLIENT ID': int, - 'CLIENT KILL': bool_ok, + 'CLIENT KILL': lambda r: int or nativestr(r) == 'OK', 'CLIENT LIST': parse_client_list, 'CLIENT SETNAME': bool_ok, 'CLIENT UNBLOCK': lambda r: r and int(r) == 1 or False, @@ -790,6 +790,40 @@ class Redis(object): "Disconnects the client at ``address`` (ip:port)" return self.execute_command('CLIENT KILL', address) + def client_kill_filter(self, *filter_options): + """ + Disconnects the client using a variety of filter options" + :param filter_options: a tuple or list of filter options with the following format: + (filter, value, filter, value,...) or + [filter, value, filter, value,...] + """ + if len(filter_options) == 1 and isinstance(filter_options[0], basestring): + return self.client_kill(filter_options[0]) + if not isinstance(filter_options, (list, tuple)) or not filter_options: + raise DataError("CLIENT KILL ... ... " + + "must be a non empty list or " + "tuple to execute") + if len(filter_options) % 2 != 0: + raise DataError("CLIENT KILL requires a filter and a value pair. Got %r" % ( + filter_options,)) + filter_types = ('addr', 'id', 'type', 'skipme') + client_types = ('normal', 'master', 'slave', 'pubsub') + yes_no = ('yes', 'no') + for index in range(0, len(filter_options), 2): + option = filter_options[index] + value = filter_options[index + 1] + key = str(option).lower() + if key not in filter_types: + raise DataError("CLIENT KILL must be one of %r" % ( + filter_types,)) + if key == 'type' and option not in client_types: + raise DataError("CLIENT KILL TYPE must be one of %r" % ( + client_types,)) + if key == 'skipme' and value not in yes_no: + raise DataError("CLIENT KILL SKIPME must be one of %r" % ( + yes_no,)) + return self.execute_command('CLIENT KILL', *filter_options) + def client_list(self, _type=None): """ Returns a list of currently connected clients. -- cgit v1.2.1 From 1861986a39f8658150eaa8a61b367e50c7145355 Mon Sep 17 00:00:00 2001 From: Theo Despoudis Date: Tue, 4 Dec 2018 15:22:21 +0000 Subject: Removed ambiguous argument handling Signed-off-by: Theo Despoudis --- redis/client.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'redis/client.py') diff --git a/redis/client.py b/redis/client.py index 6791d67..035c56c 100755 --- a/redis/client.py +++ b/redis/client.py @@ -797,8 +797,6 @@ class Redis(object): (filter, value, filter, value,...) or [filter, value, filter, value,...] """ - if len(filter_options) == 1 and isinstance(filter_options[0], basestring): - return self.client_kill(filter_options[0]) if not isinstance(filter_options, (list, tuple)) or not filter_options: raise DataError("CLIENT KILL ... ... " + "must be a non empty list or " -- cgit v1.2.1 From f91a9d522af9eb808f4b692a5fe24d70431ac8c3 Mon Sep 17 00:00:00 2001 From: Theo Despoudis Date: Tue, 4 Dec 2018 15:30:39 +0000 Subject: Simplified key, value handling inside for-range loop Signed-off-by: Theo Despoudis --- redis/client.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'redis/client.py') diff --git a/redis/client.py b/redis/client.py index 035c56c..c329ea9 100755 --- a/redis/client.py +++ b/redis/client.py @@ -808,13 +808,12 @@ class Redis(object): client_types = ('normal', 'master', 'slave', 'pubsub') yes_no = ('yes', 'no') for index in range(0, len(filter_options), 2): - option = filter_options[index] - value = filter_options[index + 1] - key = str(option).lower() + key = str(filter_options[index]).lower() + value = str(filter_options[index + 1]).lower() if key not in filter_types: raise DataError("CLIENT KILL must be one of %r" % ( filter_types,)) - if key == 'type' and option not in client_types: + if key == 'type' and value not in client_types: raise DataError("CLIENT KILL TYPE must be one of %r" % ( client_types,)) if key == 'skipme' and value not in yes_no: -- cgit v1.2.1 From 4df86b82e78f78f1fd5c010687e5a097df0e0bab Mon Sep 17 00:00:00 2001 From: Theo Despoudis Date: Tue, 4 Dec 2018 16:40:51 +0000 Subject: Fixes codestyle issues Signed-off-by: Theo Despoudis --- redis/client.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'redis/client.py') diff --git a/redis/client.py b/redis/client.py index c329ea9..644165c 100755 --- a/redis/client.py +++ b/redis/client.py @@ -471,7 +471,7 @@ class Redis(object): { 'CLIENT GETNAME': lambda r: r and nativestr(r), 'CLIENT ID': int, - 'CLIENT KILL': lambda r: int or nativestr(r) == 'OK', + 'CLIENT KILL': lambda r: int or nativestr(r) == 'OK', 'CLIENT LIST': parse_client_list, 'CLIENT SETNAME': bool_ok, 'CLIENT UNBLOCK': lambda r: r and int(r) == 1 or False, @@ -792,18 +792,20 @@ class Redis(object): def client_kill_filter(self, *filter_options): """ - Disconnects the client using a variety of filter options" - :param filter_options: a tuple or list of filter options with the following format: + Disconnects the client using a variety of filter options + :param filter_options: a tuple or list of filter options with + the following format: (filter, value, filter, value,...) or [filter, value, filter, value,...] """ if not isinstance(filter_options, (list, tuple)) or not filter_options: - raise DataError("CLIENT KILL ... ... " + + raise DataError("CLIENT KILL ... ... " + " " "must be a non empty list or " "tuple to execute") if len(filter_options) % 2 != 0: - raise DataError("CLIENT KILL requires a filter and a value pair. Got %r" % ( - filter_options,)) + raise DataError("CLIENT KILL requires a filter " + "and a value pair. Got %r" % (filter_options,)) filter_types = ('addr', 'id', 'type', 'skipme') client_types = ('normal', 'master', 'slave', 'pubsub') yes_no = ('yes', 'no') @@ -814,11 +816,11 @@ class Redis(object): raise DataError("CLIENT KILL must be one of %r" % ( filter_types,)) if key == 'type' and value not in client_types: - raise DataError("CLIENT KILL TYPE must be one of %r" % ( - client_types,)) + raise DataError("CLIENT KILL TYPE " + "must be one of %r" % (client_types,)) if key == 'skipme' and value not in yes_no: - raise DataError("CLIENT KILL SKIPME must be one of %r" % ( - yes_no,)) + raise DataError("CLIENT KILL SKIPME " + "must be one of %r" % (yes_no,)) return self.execute_command('CLIENT KILL', *filter_options) def client_list(self, _type=None): -- cgit v1.2.1 From c01ac7d9a67d8a36f7ed6edfcf2f68d27658eb17 Mon Sep 17 00:00:00 2001 From: Theo Despoudis Date: Thu, 6 Dec 2018 22:42:02 +0000 Subject: Code review fixes Signed-off-by: Theo Despoudis --- redis/client.py | 67 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 29 deletions(-) (limited to 'redis/client.py') diff --git a/redis/client.py b/redis/client.py index 644165c..3cf37be 100755 --- a/redis/client.py +++ b/redis/client.py @@ -413,6 +413,12 @@ def parse_pubsub_numsub(response, **options): return list(zip(response[0::2], response[1::2])) +def parse_client_kill(response, **options): + if isinstance(response, (long, int)): + return int(response) + return nativestr(response) == 'OK' + + class Redis(object): """ Implementation of the Redis protocol. @@ -471,7 +477,7 @@ class Redis(object): { 'CLIENT GETNAME': lambda r: r and nativestr(r), 'CLIENT ID': int, - 'CLIENT KILL': lambda r: int or nativestr(r) == 'OK', + 'CLIENT KILL': parse_client_kill, 'CLIENT LIST': parse_client_list, 'CLIENT SETNAME': bool_ok, 'CLIENT UNBLOCK': lambda r: r and int(r) == 1 or False, @@ -790,37 +796,40 @@ class Redis(object): "Disconnects the client at ``address`` (ip:port)" return self.execute_command('CLIENT KILL', address) - def client_kill_filter(self, *filter_options): + def client_kill_filter(self, _id=None, _type=None, addr=None, skipme=None): """ Disconnects the client using a variety of filter options - :param filter_options: a tuple or list of filter options with - the following format: - (filter, value, filter, value,...) or - [filter, value, filter, value,...] - """ - if not isinstance(filter_options, (list, tuple)) or not filter_options: + :param _id: Allows to kill a client by its unique ID field + :param _type: Kill by type. Where type is one of normal, + master, slave and pubsub + :param addr: This is exactly the same as the client_kill + :param skipme: If yes, then he client calling the command + will not get killed. + No will have the effect of also killing the client calling the command. + """ + filter_options = () + 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,)) + filter_options = filter_options + (Token.get_token('TYPE'), _type) + if skipme is not None: + yes_no = ('yes', 'no') + if str(skipme).lower() not in yes_no: + raise DataError("CLIENT KILL skipme yes/no must be one of %r" + % (yes_no,)) + filter_options = filter_options + (Token.get_token('SKIPME'), + skipme) + if _id is not None: + filter_options = filter_options + (Token.get_token('ID'), + _id) + if addr is not None: + filter_options = filter_options + (Token.get_token('ADDR'), + addr) + if len(filter_options) == 0: raise DataError("CLIENT KILL ... ... " - " " - "must be a non empty list or " - "tuple to execute") - if len(filter_options) % 2 != 0: - raise DataError("CLIENT KILL requires a filter " - "and a value pair. Got %r" % (filter_options,)) - filter_types = ('addr', 'id', 'type', 'skipme') - client_types = ('normal', 'master', 'slave', 'pubsub') - yes_no = ('yes', 'no') - for index in range(0, len(filter_options), 2): - key = str(filter_options[index]).lower() - value = str(filter_options[index + 1]).lower() - if key not in filter_types: - raise DataError("CLIENT KILL must be one of %r" % ( - filter_types,)) - if key == 'type' and value not in client_types: - raise DataError("CLIENT KILL TYPE " - "must be one of %r" % (client_types,)) - if key == 'skipme' and value not in yes_no: - raise DataError("CLIENT KILL SKIPME " - "must be one of %r" % (yes_no,)) + " must specify at least one filter") return self.execute_command('CLIENT KILL', *filter_options) def client_list(self, _type=None): -- cgit v1.2.1 From 8c0af5b9798965219e75731e30d2dc192956f4b5 Mon Sep 17 00:00:00 2001 From: Andy McCurdy Date: Mon, 17 Dec 2018 12:48:14 -0800 Subject: small cleanup of client_kill_filter changed skipme to a bool use a list to accumulate filter options --- redis/client.py | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) (limited to 'redis/client.py') diff --git a/redis/client.py b/redis/client.py index 3cf37be..459786b 100755 --- a/redis/client.py +++ b/redis/client.py @@ -798,39 +798,37 @@ class Redis(object): def client_kill_filter(self, _id=None, _type=None, addr=None, skipme=None): """ - Disconnects the client using a variety of filter options - :param _id: Allows to kill a client by its unique ID field - :param _type: Kill by type. Where type is one of normal, - master, slave and pubsub - :param addr: This is exactly the same as the client_kill - :param skipme: If yes, then he client calling the command - will not get killed. - No will have the effect of also killing the client calling the command. - """ - filter_options = () + Disconnects client(s) using a variety of filter options + :param id: Kills a client by its unique ID field + :param type: Kills a client by type where type is one of 'normal', + 'master', 'slave' or 'pubsub' + :param addr: Kills a client by its 'address:port' + :param skipme: If True, then the client calling the command + will not get killed even if it is identified by one of the filter + options. If skipme is not provided, the server defaults to skipme=True + """ + args = [] 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,)) - filter_options = filter_options + (Token.get_token('TYPE'), _type) + args.extend((Token.get_token('TYPE'), _type)) if skipme is not None: - yes_no = ('yes', 'no') - if str(skipme).lower() not in yes_no: - raise DataError("CLIENT KILL skipme yes/no must be one of %r" - % (yes_no,)) - filter_options = filter_options + (Token.get_token('SKIPME'), - skipme) + if not isinstance(skipme, bool): + raise DataError("CLIENT KILL skipme must be a bool") + if skipme: + args.extend((Token.get_token('SKIPME'), Token.get_token('YES')) + else: + args.extend((Token.get_token('SKIPME'), Token.get_token('NO')) if _id is not None: - filter_options = filter_options + (Token.get_token('ID'), - _id) + args.extend((Token.get_token('ID'), _id)) if addr is not None: - filter_options = filter_options + (Token.get_token('ADDR'), - addr) - if len(filter_options) == 0: + args.extend((Token.get_token('ADDR'), addr)) + if not args: raise DataError("CLIENT KILL ... ... " " must specify at least one filter") - return self.execute_command('CLIENT KILL', *filter_options) + return self.execute_command('CLIENT KILL', *args) def client_list(self, _type=None): """ -- cgit v1.2.1 From 460db073d7246a2ada5dac90985e289a2e0c20f9 Mon Sep 17 00:00:00 2001 From: Andy McCurdy Date: Mon, 17 Dec 2018 12:53:38 -0800 Subject: fix missing parens --- redis/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'redis/client.py') diff --git a/redis/client.py b/redis/client.py index 459786b..9101abd 100755 --- a/redis/client.py +++ b/redis/client.py @@ -818,9 +818,9 @@ class Redis(object): if not isinstance(skipme, bool): raise DataError("CLIENT KILL skipme must be a bool") if skipme: - args.extend((Token.get_token('SKIPME'), Token.get_token('YES')) + args.extend((Token.get_token('SKIPME'), Token.get_token('YES'))) else: - args.extend((Token.get_token('SKIPME'), Token.get_token('NO')) + args.extend((Token.get_token('SKIPME'), Token.get_token('NO'))) if _id is not None: args.extend((Token.get_token('ID'), _id)) if addr is not None: -- cgit v1.2.1 From e9e83700cc30ed4ba6f6cf4a96773390ec9d0824 Mon Sep 17 00:00:00 2001 From: Andy McCurdy Date: Mon, 17 Dec 2018 13:02:46 -0800 Subject: pep8 fix --- redis/client.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'redis/client.py') diff --git a/redis/client.py b/redis/client.py index 9101abd..51374c2 100755 --- a/redis/client.py +++ b/redis/client.py @@ -818,9 +818,11 @@ class Redis(object): if not isinstance(skipme, bool): raise DataError("CLIENT KILL skipme must be a bool") if skipme: - args.extend((Token.get_token('SKIPME'), Token.get_token('YES'))) + args.extend((Token.get_token('SKIPME'), + Token.get_token('YES'))) else: - args.extend((Token.get_token('SKIPME'), Token.get_token('NO'))) + args.extend((Token.get_token('SKIPME'), + Token.get_token('NO'))) if _id is not None: args.extend((Token.get_token('ID'), _id)) if addr is not None: -- cgit v1.2.1