4444 TimeoutSecT ,
4545 ZScoreBoundT ,
4646)
47+ from redis .utils import (
48+ deprecated_function ,
49+ extract_expire_flags ,
50+ )
4751
4852from .helpers import list_or_args
4953
@@ -1837,10 +1841,10 @@ def getdel(self, name: KeyT) -> ResponseT:
18371841 def getex (
18381842 self ,
18391843 name : KeyT ,
1840- ex : Union [ExpiryT , None ] = None ,
1841- px : Union [ExpiryT , None ] = None ,
1842- exat : Union [AbsExpiryT , None ] = None ,
1843- pxat : Union [AbsExpiryT , None ] = None ,
1844+ ex : Optional [ExpiryT ] = None ,
1845+ px : Optional [ExpiryT ] = None ,
1846+ exat : Optional [AbsExpiryT ] = None ,
1847+ pxat : Optional [AbsExpiryT ] = None ,
18441848 persist : bool = False ,
18451849 ) -> ResponseT :
18461850 """
@@ -1863,41 +1867,19 @@ def getex(
18631867
18641868 For more information see https://redis.io/commands/getex
18651869 """
1866-
18671870 opset = {ex , px , exat , pxat }
18681871 if len (opset ) > 2 or len (opset ) > 1 and persist :
18691872 raise DataError (
18701873 "``ex``, ``px``, ``exat``, ``pxat``, "
18711874 "and ``persist`` are mutually exclusive."
18721875 )
18731876
1874- pieces : list [EncodableT ] = []
1875- # similar to set command
1876- if ex is not None :
1877- pieces .append ("EX" )
1878- if isinstance (ex , datetime .timedelta ):
1879- ex = int (ex .total_seconds ())
1880- pieces .append (ex )
1881- if px is not None :
1882- pieces .append ("PX" )
1883- if isinstance (px , datetime .timedelta ):
1884- px = int (px .total_seconds () * 1000 )
1885- pieces .append (px )
1886- # similar to pexpireat command
1887- if exat is not None :
1888- pieces .append ("EXAT" )
1889- if isinstance (exat , datetime .datetime ):
1890- exat = int (exat .timestamp ())
1891- pieces .append (exat )
1892- if pxat is not None :
1893- pieces .append ("PXAT" )
1894- if isinstance (pxat , datetime .datetime ):
1895- pxat = int (pxat .timestamp () * 1000 )
1896- pieces .append (pxat )
1877+ exp_options : list [EncodableT ] = extract_expire_flags (ex , px , exat , pxat )
1878+
18971879 if persist :
1898- pieces .append ("PERSIST" )
1880+ exp_options .append ("PERSIST" )
18991881
1900- return self .execute_command ("GETEX" , name , * pieces )
1882+ return self .execute_command ("GETEX" , name , * exp_options )
19011883
19021884 def __getitem__ (self , name : KeyT ):
19031885 """
@@ -2255,14 +2237,14 @@ def set(
22552237 self ,
22562238 name : KeyT ,
22572239 value : EncodableT ,
2258- ex : Union [ExpiryT , None ] = None ,
2259- px : Union [ExpiryT , None ] = None ,
2240+ ex : Optional [ExpiryT ] = None ,
2241+ px : Optional [ExpiryT ] = None ,
22602242 nx : bool = False ,
22612243 xx : bool = False ,
22622244 keepttl : bool = False ,
22632245 get : bool = False ,
2264- exat : Union [AbsExpiryT , None ] = None ,
2265- pxat : Union [AbsExpiryT , None ] = None ,
2246+ exat : Optional [AbsExpiryT ] = None ,
2247+ pxat : Optional [AbsExpiryT ] = None ,
22662248 ) -> ResponseT :
22672249 """
22682250 Set the value at key ``name`` to ``value``
@@ -2292,36 +2274,21 @@ def set(
22922274
22932275 For more information see https://redis.io/commands/set
22942276 """
2277+ opset = {ex , px , exat , pxat }
2278+ if len (opset ) > 2 or len (opset ) > 1 and keepttl :
2279+ raise DataError (
2280+ "``ex``, ``px``, ``exat``, ``pxat``, "
2281+ "and ``keepttl`` are mutually exclusive."
2282+ )
2283+
2284+ if nx and xx :
2285+ raise DataError ("``nx`` and ``xx`` are mutually exclusive." )
2286+
22952287 pieces : list [EncodableT ] = [name , value ]
22962288 options = {}
2297- if ex is not None :
2298- pieces .append ("EX" )
2299- if isinstance (ex , datetime .timedelta ):
2300- pieces .append (int (ex .total_seconds ()))
2301- elif isinstance (ex , int ):
2302- pieces .append (ex )
2303- elif isinstance (ex , str ) and ex .isdigit ():
2304- pieces .append (int (ex ))
2305- else :
2306- raise DataError ("ex must be datetime.timedelta or int" )
2307- if px is not None :
2308- pieces .append ("PX" )
2309- if isinstance (px , datetime .timedelta ):
2310- pieces .append (int (px .total_seconds () * 1000 ))
2311- elif isinstance (px , int ):
2312- pieces .append (px )
2313- else :
2314- raise DataError ("px must be datetime.timedelta or int" )
2315- if exat is not None :
2316- pieces .append ("EXAT" )
2317- if isinstance (exat , datetime .datetime ):
2318- exat = int (exat .timestamp ())
2319- pieces .append (exat )
2320- if pxat is not None :
2321- pieces .append ("PXAT" )
2322- if isinstance (pxat , datetime .datetime ):
2323- pxat = int (pxat .timestamp () * 1000 )
2324- pieces .append (pxat )
2289+
2290+ pieces .extend (extract_expire_flags (ex , px , exat , pxat ))
2291+
23252292 if keepttl :
23262293 pieces .append ("KEEPTTL" )
23272294
@@ -4980,6 +4947,65 @@ def hgetall(self, name: str) -> Union[Awaitable[dict], dict]:
49804947 """
49814948 return self .execute_command ("HGETALL" , name , keys = [name ])
49824949
4950+ def hgetdel (
4951+ self , name : str , * keys : str
4952+ ) -> Union [Awaitable [Optional [str ]], Optional [str ]]:
4953+ """
4954+ Return the value of ``key`` within the hash ``name`` and
4955+ delete the field in the hash.
4956+ This command is similar to HGET, except for the fact that it also deletes
4957+ the key on success from the hash with the provided ```name```.
4958+
4959+ Available since Redis 8.0
4960+ For more information see https://redis.io/commands/hgetdel
4961+ """
4962+ return self .execute_command ("HGETDEL" , name , "FIELDS" , len (keys ), * keys )
4963+
4964+ def hgetex (
4965+ self ,
4966+ name : KeyT ,
4967+ * keys : str ,
4968+ ex : Optional [ExpiryT ] = None ,
4969+ px : Optional [ExpiryT ] = None ,
4970+ exat : Optional [AbsExpiryT ] = None ,
4971+ pxat : Optional [AbsExpiryT ] = None ,
4972+ persist : bool = False ,
4973+ ) -> Union [Awaitable [Optional [str ]], Optional [str ]]:
4974+ """
4975+ Return the values of ``keys`` within the hash ``name``
4976+ and optionally set their expiration.
4977+
4978+ ``ex`` sets an expire flag on ``kyes`` for ``ex`` seconds.
4979+
4980+ ``px`` sets an expire flag on ``keys`` for ``px`` milliseconds.
4981+
4982+ ``exat`` sets an expire flag on ``keys`` for ``ex`` seconds,
4983+ specified in unix time.
4984+
4985+ ``pxat`` sets an expire flag on ``keys`` for ``ex`` milliseconds,
4986+ specified in unix time.
4987+
4988+ ``persist`` remove the time to live associated with the ``keys``.
4989+
4990+ Available since Redis 8.0
4991+ For more information see https://redis.io/commands/hgetex
4992+ """
4993+ opset = {ex , px , exat , pxat }
4994+ if len (opset ) > 2 or len (opset ) > 1 and persist :
4995+ raise DataError (
4996+ "``ex``, ``px``, ``exat``, ``pxat``, "
4997+ "and ``persist`` are mutually exclusive."
4998+ )
4999+
5000+ exp_options : list [EncodableT ] = extract_expire_flags (ex , px , exat , pxat )
5001+
5002+ if persist :
5003+ exp_options .append ("PERSIST" )
5004+
5005+ return self .execute_command (
5006+ "HGETEX" , name , * exp_options , "FIELDS" , len (keys ), * keys
5007+ )
5008+
49835009 def hincrby (
49845010 self , name : str , key : str , amount : int = 1
49855011 ) -> Union [Awaitable [int ], int ]:
@@ -5047,6 +5073,87 @@ def hset(
50475073
50485074 return self .execute_command ("HSET" , name , * pieces )
50495075
5076+ def hsetex (
5077+ self ,
5078+ name : str ,
5079+ key : Optional [str ] = None ,
5080+ value : Optional [str ] = None ,
5081+ mapping : Optional [dict ] = None ,
5082+ items : Optional [list ] = None ,
5083+ ex : Optional [ExpiryT ] = None ,
5084+ px : Optional [ExpiryT ] = None ,
5085+ exat : Optional [AbsExpiryT ] = None ,
5086+ pxat : Optional [AbsExpiryT ] = None ,
5087+ fnx : bool = False ,
5088+ fxx : bool = False ,
5089+ keepttl : bool = False ,
5090+ ) -> Union [Awaitable [int ], int ]:
5091+ """
5092+ Set ``key`` to ``value`` within hash ``name``
5093+
5094+ ``mapping`` accepts a dict of key/value pairs that will be
5095+ added to hash ``name``.
5096+
5097+ ``items`` accepts a list of key/value pairs that will be
5098+ added to hash ``name``.
5099+
5100+ ``ex`` sets an expire flag on ``keys`` for ``ex`` seconds.
5101+
5102+ ``px`` sets an expire flag on ``keys`` for ``px`` milliseconds.
5103+
5104+ ``exat`` sets an expire flag on ``keys`` for ``ex`` seconds,
5105+ specified in unix time.
5106+
5107+ ``pxat`` sets an expire flag on ``keys`` for ``ex`` milliseconds,
5108+ specified in unix time.
5109+
5110+ ``fnx`` if set to True, set the value for each provided key to each
5111+ provided value only if all do not already exist.
5112+
5113+ ``fxx`` if set to True, set the value for each provided key to each
5114+ provided value only if all already exist.
5115+
5116+ ``keepttl`` if True, retain the time to live associated with the keys.
5117+
5118+ Returns the number of fields that were added.
5119+
5120+ Available since Redis 8.0
5121+ For more information see https://redis.io/commands/hsetex
5122+ """
5123+ if key is None and not mapping and not items :
5124+ raise DataError ("'hsetex' with no key value pairs" )
5125+
5126+ opset = {ex , px , exat , pxat }
5127+ if len (opset ) > 2 or len (opset ) > 1 and keepttl :
5128+ raise DataError (
5129+ "``ex``, ``px``, ``exat``, ``pxat``, "
5130+ "and ``keepttl`` are mutually exclusive."
5131+ )
5132+
5133+ if fnx and fxx :
5134+ raise DataError ("``fnx`` and ``fxx`` are mutually exclusive." )
5135+
5136+ exp_options : list [EncodableT ] = extract_expire_flags (ex , px , exat , pxat )
5137+ if fnx :
5138+ exp_options .append ("FNX" )
5139+ if fxx :
5140+ exp_options .append ("FXX" )
5141+ if keepttl :
5142+ exp_options .append ("KEEPTTL" )
5143+
5144+ pieces = []
5145+ if items :
5146+ pieces .extend (items )
5147+ if key is not None :
5148+ pieces .extend ((key , value ))
5149+ if mapping :
5150+ for pair in mapping .items ():
5151+ pieces .extend (pair )
5152+
5153+ return self .execute_command (
5154+ "HSETEX" , name , * exp_options , "FIELDS" , int (len (pieces ) / 2 ), * pieces
5155+ )
5156+
50505157 def hsetnx (self , name : str , key : str , value : str ) -> Union [Awaitable [bool ], bool ]:
50515158 """
50525159 Set ``key`` to ``value`` within hash ``name`` if ``key`` does not
@@ -5056,19 +5163,18 @@ def hsetnx(self, name: str, key: str, value: str) -> Union[Awaitable[bool], bool
50565163 """
50575164 return self .execute_command ("HSETNX" , name , key , value )
50585165
5166+ @deprecated_function (
5167+ version = "4.0.0" ,
5168+ reason = "Use 'hset' instead." ,
5169+ name = "hmset" ,
5170+ )
50595171 def hmset (self , name : str , mapping : dict ) -> Union [Awaitable [str ], str ]:
50605172 """
50615173 Set key to value within hash ``name`` for each corresponding
50625174 key and value from the ``mapping`` dict.
50635175
50645176 For more information see https://redis.io/commands/hmset
50655177 """
5066- warnings .warn (
5067- f"{ self .__class__ .__name__ } .hmset() is deprecated. "
5068- f"Use { self .__class__ .__name__ } .hset() instead." ,
5069- DeprecationWarning ,
5070- stacklevel = 2 ,
5071- )
50725178 if not mapping :
50735179 raise DataError ("'hmset' with 'mapping' of length 0" )
50745180 items = []
0 commit comments