From c738ffdf7aa3f497e0af213b418b704ab27b0a86 Mon Sep 17 00:00:00 2001 From: dogukanteber Date: Fri, 25 Feb 2022 22:25:48 +0300 Subject: [PATCH 1/5] Add support for EXPIREAT command's options --- redis/commands/core.py | 17 ++++++++++++++--- tests/test_commands.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/redis/commands/core.py b/redis/commands/core.py index 968b7cc412..8233e23993 100644 --- a/redis/commands/core.py +++ b/redis/commands/core.py @@ -1501,16 +1501,27 @@ def expire(self, name: KeyT, time: ExpiryT) -> ResponseT: time = int(time.total_seconds()) return self.execute_command("EXPIRE", name, time) - def expireat(self, name: KeyT, when: AbsExpiryT) -> ResponseT: + def expireat(self, name: KeyT, when: AbsExpiryT, option: str = None) -> ResponseT: """ - Set an expire flag on key ``name``. ``when`` can be represented + Set an expire flag on key ``name`` with the given ``option``. ``when`` can be represented as an integer indicating unix time or a Python datetime object. + Valid options are: + -> NX -- Set expiry only when the key has no expiry + -> XX -- Set expiry only when the key has an existing expiry + -> GT -- Set expiry only when the new expiry is greater than current one + -> LT -- Set expiry only when the new expiry is less than current one + For more information check https://redis.io/commands/expireat """ if isinstance(when, datetime.datetime): when = int(time.mktime(when.timetuple())) - return self.execute_command("EXPIREAT", name, when) + + exp_option = list() + if option is not None: + exp_option.append(option) + + return self.execute_command("EXPIREAT", name, when, *exp_option) def get(self, name: KeyT) -> ResponseT: """ diff --git a/tests/test_commands.py b/tests/test_commands.py index 5c32d5f296..c2155aa6c6 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -1059,6 +1059,43 @@ def test_expireat_unixtime(self, r): assert r.expireat("a", expire_at_seconds) is True assert 0 < r.ttl("a") <= 61 + @skip_if_server_version_lt("7.0.0") + def test_expireat_option_nx(self, r): + assert r.set("key", "val") is True + expire_at = redis_server_time(r) + datetime.timedelta(minutes=1) + assert r.expireat("key", expire_at, "NX") is True + expire_at = redis_server_time(r) + datetime.timedelta(minutes=2) + assert r.expireat("key", expire_at, "NX") is False + + @skip_if_server_version_lt("7.0.0") + def test_expireat_option_xx(self, r): + assert r.set("key", "val") is True + expire_at = redis_server_time(r) + datetime.timedelta(minutes=1) + assert r.expireat("key", expire_at, "XX") is False + assert r.expireat("key", expire_at) is True + expire_at = redis_server_time(r) + datetime.timedelta(minutes=2) + assert r.expireat("key", expire_at, "XX") is True + + @skip_if_server_version_lt("7.0.0") + def test_expireat_option_gt(self, r): + expire_at = redis_server_time(r) + datetime.timedelta(minutes=2) + assert r.set("key", "val") is True + assert r.expireat("key", expire_at) is True + expire_at = redis_server_time(r) + datetime.timedelta(minutes=1) + assert r.expireat("key", expire_at, "GT") is False + expire_at = redis_server_time(r) + datetime.timedelta(minutes=3) + assert r.expireat("key", expire_at, "GT") is True + + @skip_if_server_version_lt("7.0.0") + def test_expireat_option_lt(self, r): + expire_at = redis_server_time(r) + datetime.timedelta(minutes=2) + assert r.set("key", "val") is True + assert r.expireat("key", expire_at) is True + expire_at = redis_server_time(r) + datetime.timedelta(minutes=3) + assert r.expireat("key", expire_at, "LT") is False + expire_at = redis_server_time(r) + datetime.timedelta(minutes=1) + assert r.expireat("key", expire_at, "LT") is True + def test_get_and_set(self, r): # get and set can't be tested independently of each other assert r.get("a") is None From a6c4a56c1228ce6e6d613fc2539041cc5fce2c48 Mon Sep 17 00:00:00 2001 From: dogukanteber Date: Fri, 25 Feb 2022 22:34:12 +0300 Subject: [PATCH 2/5] Fix linter errors --- redis/commands/core.py | 5 +++-- tests/test_commands.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/redis/commands/core.py b/redis/commands/core.py index 8233e23993..f3727dce12 100644 --- a/redis/commands/core.py +++ b/redis/commands/core.py @@ -1503,8 +1503,9 @@ def expire(self, name: KeyT, time: ExpiryT) -> ResponseT: def expireat(self, name: KeyT, when: AbsExpiryT, option: str = None) -> ResponseT: """ - Set an expire flag on key ``name`` with the given ``option``. ``when`` can be represented - as an integer indicating unix time or a Python datetime object. + Set an expire flag on key ``name`` with the given ``option``. ``when`` + can be represented as an integer indicating unix time or a Python + datetime object. Valid options are: -> NX -- Set expiry only when the key has no expiry diff --git a/tests/test_commands.py b/tests/test_commands.py index c2155aa6c6..f367b3efab 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -1063,7 +1063,7 @@ def test_expireat_unixtime(self, r): def test_expireat_option_nx(self, r): assert r.set("key", "val") is True expire_at = redis_server_time(r) + datetime.timedelta(minutes=1) - assert r.expireat("key", expire_at, "NX") is True + assert r.expireat("key", expire_at, "NX") is True expire_at = redis_server_time(r) + datetime.timedelta(minutes=2) assert r.expireat("key", expire_at, "NX") is False From 9a40a6cce96d95f39b8ddd527b6c80467fa13071 Mon Sep 17 00:00:00 2001 From: dogukanteber Date: Tue, 8 Mar 2022 23:16:30 +0300 Subject: [PATCH 3/5] Make changes on method arguments --- redis/commands/core.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/redis/commands/core.py b/redis/commands/core.py index f3727dce12..4836caf026 100644 --- a/redis/commands/core.py +++ b/redis/commands/core.py @@ -1501,9 +1501,15 @@ def expire(self, name: KeyT, time: ExpiryT) -> ResponseT: time = int(time.total_seconds()) return self.execute_command("EXPIRE", name, time) - def expireat(self, name: KeyT, when: AbsExpiryT, option: str = None) -> ResponseT: + def expireat(self, + name: KeyT, + when: AbsExpiryT, + option: Union[ + Literal["NX"], Literal["XX"], Literal["GT"], Literal["LT"] + ] = None, + ) -> ResponseT: """ - Set an expire flag on key ``name`` with the given ``option``. ``when`` + Set an expire flag on key ``name`` with given ``option``. ``when`` can be represented as an integer indicating unix time or a Python datetime object. @@ -1518,6 +1524,10 @@ def expireat(self, name: KeyT, when: AbsExpiryT, option: str = None) -> Response if isinstance(when, datetime.datetime): when = int(time.mktime(when.timetuple())) + options = ["NX", "XX", "GT", "LT"] + if option and option not in options: + raise DataError(f"OPTION must be one of {options}") + exp_option = list() if option is not None: exp_option.append(option) From fcdde6e50204172b917314fd57b66900899e0226 Mon Sep 17 00:00:00 2001 From: dogukanteber Date: Tue, 8 Mar 2022 23:30:38 +0300 Subject: [PATCH 4/5] Fix linter errors --- redis/commands/core.py | 3 ++- tests/test_commands.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/redis/commands/core.py b/redis/commands/core.py index 5aee0a2401..b2c2b375bf 100644 --- a/redis/commands/core.py +++ b/redis/commands/core.py @@ -1512,7 +1512,8 @@ def expire(self, name: KeyT, time: ExpiryT) -> ResponseT: time = int(time.total_seconds()) return self.execute_command("EXPIRE", name, time) - def expireat(self, + def expireat( + self, name: KeyT, when: AbsExpiryT, option: Union[ diff --git a/tests/test_commands.py b/tests/test_commands.py index 48b651f374..60fbb3ee9e 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -1075,11 +1075,12 @@ def test_expireat_unixtime(self, r): assert r.expireat("a", expire_at_seconds) is True assert 0 < r.ttl("a") <= 61 + @skip_if_server_version_lt("7.0.0") def test_expiretime(self, r): r.set("a", "foo") r.expireat("a", 33177117420) assert r.expiretime("a") == 33177117420 - + @skip_if_server_version_lt("7.0.0") def test_expireat_option_nx(self, r): assert r.set("key", "val") is True From c368c35c675e42d8355f5644c4015fe05ccfa11f Mon Sep 17 00:00:00 2001 From: dvora-h Date: Mon, 14 Mar 2022 12:46:52 +0200 Subject: [PATCH 5/5] add variables to the function header --- redis/commands/core.py | 21 ++++++++++++--------- tests/test_commands.py | 16 ++++++++-------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/redis/commands/core.py b/redis/commands/core.py index b2c2b375bf..406a159538 100644 --- a/redis/commands/core.py +++ b/redis/commands/core.py @@ -1516,9 +1516,10 @@ def expireat( self, name: KeyT, when: AbsExpiryT, - option: Union[ - Literal["NX"], Literal["XX"], Literal["GT"], Literal["LT"] - ] = None, + nx: bool = False, + xx: bool = False, + gt: bool = False, + lt: bool = False, ) -> ResponseT: """ Set an expire flag on key ``name`` with given ``option``. ``when`` @@ -1536,13 +1537,15 @@ def expireat( if isinstance(when, datetime.datetime): when = int(time.mktime(when.timetuple())) - options = ["NX", "XX", "GT", "LT"] - if option and option not in options: - raise DataError(f"OPTION must be one of {options}") - exp_option = list() - if option is not None: - exp_option.append(option) + if nx: + exp_option.append("NX") + if xx: + exp_option.append("XX") + if gt: + exp_option.append("GT") + if lt: + exp_option.append("LT") return self.execute_command("EXPIREAT", name, when, *exp_option) diff --git a/tests/test_commands.py b/tests/test_commands.py index 60fbb3ee9e..a7b3a89c1c 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -1085,18 +1085,18 @@ def test_expiretime(self, r): def test_expireat_option_nx(self, r): assert r.set("key", "val") is True expire_at = redis_server_time(r) + datetime.timedelta(minutes=1) - assert r.expireat("key", expire_at, "NX") is True + assert r.expireat("key", expire_at, nx=True) is True expire_at = redis_server_time(r) + datetime.timedelta(minutes=2) - assert r.expireat("key", expire_at, "NX") is False + assert r.expireat("key", expire_at, nx=True) is False @skip_if_server_version_lt("7.0.0") def test_expireat_option_xx(self, r): assert r.set("key", "val") is True expire_at = redis_server_time(r) + datetime.timedelta(minutes=1) - assert r.expireat("key", expire_at, "XX") is False + assert r.expireat("key", expire_at, xx=True) is False assert r.expireat("key", expire_at) is True expire_at = redis_server_time(r) + datetime.timedelta(minutes=2) - assert r.expireat("key", expire_at, "XX") is True + assert r.expireat("key", expire_at, xx=True) is True @skip_if_server_version_lt("7.0.0") def test_expireat_option_gt(self, r): @@ -1104,9 +1104,9 @@ def test_expireat_option_gt(self, r): assert r.set("key", "val") is True assert r.expireat("key", expire_at) is True expire_at = redis_server_time(r) + datetime.timedelta(minutes=1) - assert r.expireat("key", expire_at, "GT") is False + assert r.expireat("key", expire_at, gt=True) is False expire_at = redis_server_time(r) + datetime.timedelta(minutes=3) - assert r.expireat("key", expire_at, "GT") is True + assert r.expireat("key", expire_at, gt=True) is True @skip_if_server_version_lt("7.0.0") def test_expireat_option_lt(self, r): @@ -1114,9 +1114,9 @@ def test_expireat_option_lt(self, r): assert r.set("key", "val") is True assert r.expireat("key", expire_at) is True expire_at = redis_server_time(r) + datetime.timedelta(minutes=3) - assert r.expireat("key", expire_at, "LT") is False + assert r.expireat("key", expire_at, lt=True) is False expire_at = redis_server_time(r) + datetime.timedelta(minutes=1) - assert r.expireat("key", expire_at, "LT") is True + assert r.expireat("key", expire_at, lt=True) is True def test_get_and_set(self, r): # get and set can't be tested independently of each other