
    Ki}O                        U d Z ddlZddlZddlZddlmZmZ ddlmZ ddlm	Z	m
Z
 ddlmZmZmZ ddlmZmZ ddlmZ dd	lmZ dd
lmZmZ ej2                  rddlmZ  ej8                  e      Zej>                  e d<    G d de
      Z!de!de"ddfdZ#i a$e%e"df   e d<    ejL                         Z'ddZ(de"ddfdZ) G d d      Z*da+ddZ,y)aS  Redis connection management.

This module is the single point of control for Redis connections, including
the fakeredis backend used for memory:// URLs.

This module is designed to be the single point of cluster-awareness, so that
other modules can remain simple. When Redis Cluster support is added, only
this module will need to change.
    N)AsyncExitStackasynccontextmanager)TracebackType)AsyncGeneratorProtocol)ParseResulturlparse
urlunparse)ConnectionPoolRedis)PubSub)RedisCluster)
ConnectionSSLConnection)
FakeServerloggerc                       e Zd ZdZddZy)AsyncCloseablez3Protocol for objects with an async aclose() method.Nc                    K   y wN selfs    Y/home/jay/workspace/scripts/.codegraph-venv/lib/python3.12/site-packages/docket/_redis.pyaclosezAsyncCloseable.aclose"   s     s   returnN)__name__
__module____qualname____doc__r   r       r   r   r      s    ='r"   r   resourcenamer   c                    K   	 | j                          d{    y7 # t        $ r t        j                  d|d       Y yw xY ww)znClose a resource with error handling.

    Designed to be used with AsyncExitStack.push_async_callback().
    NzFailed to close %sTexc_info)r   	Exceptionr   warning)r#   r$   s     r   close_resourcer*   %   s?     
Boo B+TDABs1   A  A !A AAAr   _memory_serversc                     K   t         4 d{    t        j                          ddd      d{    y7 )7 # 1 d{  7  sw Y   yxY ww)zqClear all cached FakeServer instances.

    This is primarily for testing to ensure isolation between tests.
    N)_memory_servers_lockr+   clearr   r"   r   clear_memory_serversr/   5   sF     
 $                 s<   A7A;A9AAAAA	AurlzFakeServer | Nonec                 ,    t         j                  |       S )zpGet the cached FakeServer for a URL, if any.

    This is primarily for testing to verify server isolation.
    )r+   get)r0   s    r   get_memory_serverr3   >   s    
 s##r"   c                      e Zd ZU dZedz  ed<   edz  ed<   edz  ed<   eed<   eed<   de	d	dfd
Z
d"dZdee   dz  dedz  dedz  d	dfdZed	efd       Zed	efd       Zed	efd       Zed	edz  fd       Zde	d	e	fdZd	e	fdZd	efdZd	efdZ	 d#ded	efdZ	 d#ded	efdZed	eeez  df   fd       Zed	ee df   fd       Z!de	de	d	e"fd Z#ed	ee df   fd!       Z$y)$RedisConnectiona  Manages Redis connections for both standalone and cluster modes.

    This class encapsulates the lifecycle management of Redis connections,
    hiding whether the underlying connection is to a standalone Redis server
    or a Redis Cluster. It provides a unified interface for getting Redis
    clients, pub/sub connections, and publishing messages.

    Example:
        async with RedisConnection("redis://localhost:6379/0") as connection:
            async with connection.client() as r:
                await r.set("key", "value")
    N_connection_pool_cluster_client
_node_pool_parsed_stackr0   r   c                 \    || _         t        |      | _        d| _        d| _        d| _        y)zInitialize a Redis connection manager.

        Args:
            url: Redis URL (redis://, rediss://, redis+cluster://, or memory://)
        N)r0   r	   r9   r6   r7   r8   )r   r0   s     r   __init__zRedisConnection.__init__^   s-     } $#r"   c                    K    j                   rJ d       t                _         j                  j                          d{     j                  rƉ j                          d{    _         j                  j                   fd        j                  j                  t         j                  d        j                          _         j                  j                   fd        j                  j                  t         j                  d        S  j                          d{    _         j                  j                   fd        j                  j                  t         j                  d        S 7 ?7 7 Zw)	z+Connect to Redis when entering the context.z RedisConnection is not reentrantNc                      t         dd       S )Nr7   setattrr   s   r   <lambda>z,RedisConnection.__aenter__.<locals>.<lambda>s   s    7H$)O r"   zcluster clientc                      t         dd       S )Nr8   r?   r   s   r   rA   z,RedisConnection.__aenter__.<locals>.<lambda>y   s    |T)J r"   z	node poolc                      t         dd       S )Nr6   r?   r   s   r   rA   z,RedisConnection.__aenter__.<locals>.<lambda>   s    7I4)P r"   zconnection pool)is_connectedr   r:   
__aenter__
is_cluster_create_cluster_clientr7   callbackpush_async_callbackr*   _create_node_poolr8   _connection_pool_from_urlr6   r   s   `r   rE   zRedisConnection.__aenter__j   s6    $$H&HH$$&kk$$&&&??)-)D)D)F#FD KK  !OPKK++ 4 46F #446DOKK  !JKKK++  +/*H*H*J$JD!KK  !PQKK++ 5 57H + 	' $G %Ks8   A F
F#F
'F(CF
-F.AF
F
F
exc_typeexc_valexc_tbc                 n   K   	 | j                   j                  |||       d{    | ` y7 # | ` w xY ww)z4Close the Redis connection when exiting the context.N)r:   	__aexit__)r   rL   rM   rN   s       r   rP   zRedisConnection.__aexit__   s8     	++'''6BBB Cs    5 . ,. 5. 25c                 >    | j                   duxs | j                  duS )z'Check if the connection is established.N)r6   r7   r   s    r   rD   zRedisConnection.is_connected   s&     $$D0TD4H4HPT4TTr"   c                 2    | j                   j                  dv S )z/Check if this connection is to a Redis Cluster.)zredis+clusterrediss+clusterr9   schemer   s    r   rF   zRedisConnection.is_cluster   s     ||""&IIIr"   c                 4    | j                   j                  dk(  S )z>Check if this connection is to an in-memory fakeredis backend.memoryrT   r   s    r   	is_memoryzRedisConnection.is_memory   s     ||""h..r"   c                     | j                   S )z5Get the cluster client, if connected in cluster mode.)r7   r   s    r   cluster_clientzRedisConnection.cluster_client   s     ###r"   r$   c                 *    | j                   rd| dS |S )a  Return a prefix, hash-tagged for cluster mode key slot hashing.

        In Redis Cluster mode, keys with the same hash tag {name} are
        guaranteed to be on the same slot, which is required for multi-key
        operations.

        Args:
            name: The base name for the prefix

        Returns:
            "{name}" for cluster mode, or just "name" for standalone mode
        {})rF   )r   r$   s     r   prefixzRedisConnection.prefix   s     ??vR= r"   c                     | j                   s| j                  S | j                  j                  j	                  dd      }t        | j                  j                  |            S )ah  Convert a cluster URL to a standard Redis URL for redis-py.

        redis-py doesn't support the redis+cluster:// scheme, so we normalize
        it to redis:// (or rediss://) before passing to RedisCluster.from_url().

        Returns:
            The URL with +cluster removed from the scheme if cluster mode,
            otherwise the original URL
        z+cluster )rU   )rF   r0   r9   rU   replacer
   _replace)r   
new_schemes     r   _normalized_urlzRedisConnection._normalized_url   sM     88O\\((00R@
$,,//z/BCCr"   c                    K   t        j                  | j                               }|j                          d{    |S 7 w)zCreate and initialize an async RedisCluster client.

        Returns:
            An initialized RedisCluster client ready for use
        N)r   from_urlrd   
initialize)r   clients     r   rG   z&RedisConnection._create_cluster_client   s=       ,44T5I5I5KL!!! 	"s   7AA Ac                 t   | j                   J | j                   j                         }|st        d      |d   }t        |j                  t        |j                        | j                  j                  | j                  j                  | j                  j                  dk(  rt        d      S t        d      S )a  Create a connection pool to a cluster node for pub/sub operations.

        Redis Cluster doesn't natively support pub/sub through the cluster client,
        so we create a regular connection pool connected to one of the primary nodes.
        This pool persists for the lifetime of the RedisConnection.

        Returns:
            A ConnectionPool connected to a cluster primary node
        z%No primary nodes available in clusterr   rS   F)hostportusernamepasswordconnection_classdecode_responses)r7   get_primariesRuntimeErrorr   rj   intrk   r9   rl   rm   rU   r   r   )r   nodesnodes      r   rJ   z!RedisConnection._create_node_pool   s     ##///$$224FGGQxTYY\\**\\**||""&66 + #	
 		
 "	
 		
r"   ro   c                    K   | j                   r| j                  |       d{   S t        j                  | j                  |      S 7 %w)a4  Create a Redis connection pool from the URL.

        Handles real Redis (redis://) and in-memory fakeredis (memory://).

        Args:
            decode_responses: If True, decode Redis responses from bytes to strings

        Returns:
            A ConnectionPool ready for use with Redis clients
        N)ro   )rX   _memory_connection_poolr   rf   r0   )r   ro   s     r   rK   z)RedisConnection._connection_pool_from_url   sB      >>556FGGG&&txxBRSS Hs   !AA	&Ac                   K   ddl m}m} t                t        j                  | j                        }|t        |||      S t        4 d{    t        j                  | j                        }|t        |||      cddd      d{    S  |       }|t        | j                  <   t        |||      cddd      d{    S 7 }7 @7 	# 1 d{  7  sw Y   yxY ww)z=Create a connection pool for a memory:// URL using fakeredis.r   )FakeConnectionr   N)rn   serverro   )	fakeredis.aioredisrx   r   _patch_fakeredis_lua_runtimer+   r2   r0   r   r-   )r   ro   rx   r   ry   s        r   rv   z'RedisConnection._memory_connection_pool   s      	B 	%& !$$TXX.!!/!1  ( 	 	$((2F!%%3!%5	 	 	  \F(.ODHH%!!/!1	 	 	 	 	 	 	 	sf   AC'CC'/CC'CC''C:C'CC'C'C'C$CC$ C'c                   K   | j                   | j                    yt        | j                        4 d{   }| ddd      d{    y7 7 # 1 d{  7  sw Y   yxY ww)z?Get a Redis client, handling both standalone and cluster modes.Nconnection_pool)r7   r   r6   )r   rs     r   rh   zRedisConnection.client   sd      +&&&T-B-BC  q      sD   7A-AA-AA-AA-A-A*A!A*&A-c                  K   | j                   /| j                         4 d{   }| ddd      d{    yt        | j                        4 d{   }|j	                         4 d{   }| ddd      d{    ddd      d{    y7 x7 e# 1 d{  7  sw Y   yxY w7 \7 E7 2# 1 d{  7  sw Y   BxY w7 9# 1 d{  7  sw Y   yxY ww)zEGet a pub/sub connection, handling both standalone and cluster modes.Nr}   )r7   _cluster_pubsubr   r6   pubsub)r   psr   r   s       r   r   zRedisConnection.pubsub)  s      +++-     T-B-BC ! !q88: ! ! L! !! ! !    !! ! ! ! !! ! ! !s   !C'BC'B C'BC'B5C'C1B72C5B;;CB9CC'CC'C' B2&B)'B2.C'7C9C;C	CC		CC'C$CC$ C'channelmessagec                   K   | j                   Jt        | j                        4 d{   }|j                  ||       d{   cddd      d{    S t        | j                        4 d{   }|j                  ||       d{   cddd      d{    S 7 y7 a7 S# 1 d{  7  sw Y   yxY w7 J7 27 $# 1 d{  7  sw Y   yxY ww)z'Publish a message to a pub/sub channel.Nr}   )r7   r   r8   publishr6   )r   r   r   r   s       r   r   zRedisConnection.publish4  s     +T__= 9 9YYw889 9 9 T-B-BC 9 9qYYw889 9 9989 9 9 9989 9 9 9s   'CB#CB)B%B)CB'C3B>4C7CC CCCC%B)'C)B;/B20B;7C CCC
CCCc                t  K   t        | j                        }|j                         }	 | 	 |j                          d{    	 |j                          d{    y7 # t        $ r t
        j                  dd       Y >w xY w7 -# t        $ r t
        j                  dd       Y yw xY w# 	 |j                          d{  7   n&# t        $ r t
        j                  dd       Y nw xY w	 |j                          d{  7   w # t        $ r t
        j                  dd       Y w w xY wxY ww)a  Create a pub/sub connection using the shared node pool.

        Redis Cluster doesn't natively support pub/sub through the cluster client,
        so we use a regular Redis client connected to one of the primary nodes.
        The underlying connection pool is managed by the RedisConnection lifecycle.

        Yields:
            A PubSub object connected to a cluster node
        r}   NzFailed to close cluster pubsubTr&   zFailed to close cluster client)r   r8   r   r   r(   r   r)   )r   rh   r   s      r   r   zRedisConnection._cluster_pubsub=  s     t7
	PLPmmo%%Pmmo%%	 & P?$OP & P?$OPPmmo%% P?$OPPmmo%% P?$OPs   'D8B2 A$ A"A$ 	B B
B !D8"A$ $ BD8BD8
B  B/,D8.B//D82D54CC
CD5 C1.D50C11D55DD	DD5 D2/D51D22D55D8)r   r5   )F)%r   r   r    r!   r   __annotations__r   r   r   strr<   rE   typeBaseExceptionr   rP   propertyboolrD   rF   rX   rZ   r^   rd   rG   rJ   rK   rv   r   r   r   rh   r   r   rr   r   r   r   r"   r   r5   r5   F   s    %t++!D(( %%
C 
D 
8
}%,
 %
 $	

 

 Ud U U JD J J /4 / / $t 3 $ $3 3 "D Dl 
> 
6 (-T $T	T$ (-# $#	#J nU\-A4-GH   !nVT\: ! !9S 93 93 9 P~fdl'C P Pr"   r5   Fc                  h  	
 t         ry da dd ldd lddlm ddlm} m} ddlm	 ddl
m}mmmm	m
 	 ddl
m  |t&        | ft&        fj(                        d	|d
t&        dt*        dt&        dt,        j.                  f
	
f
d       }||_        y # t$        $ r Y y w xY w)NTr   )_msgs)Intcommand)SimpleError)ScriptingCommandsMixin_check_for_lua_globals_lua_cjson_decode_lua_cjson_encode_lua_cjson_null_lua_redis_log)
LUA_MODULE)flagsr   scriptnumkeyskeys_and_argsr   c           	      	  
  |t        |      kD  r j                        |dk  r j                        j                  |      j	                         j                         }| j                  j                  |<   t         j                  d      sej                  d d       j                  _
         j                  j                  dj                   j                  D cg c]
  }| d| d c}      }j                  d| d	      }j                  d
       j                  _         |d d d d d d        t        j!                         j#                                j                  _         j                  j$                  d g j                  _        dt(        j*                  dt(        j,                  f   f fd}dt(        j*                  dt(        j,                  f   f fd}	 |        j                  _         |	        j                  _        j3                         j                  _        j3                         j                  _        j3                         j                  _         | j                  j.                   j                  j0                   j                  j4                   j                  j6                   j                  j8                          j                  j                   j                  j$                    j                  j&                  d<    j                  j                  j;                  |d |       j;                  ||d               	 j=                  |      }
        j=                  d        jS                  |
d      S c c}w # $ r}|j>                  j@                  k(  r{ jB                  dk  r jD                         j                  jF                  dk(  r/ jH                  jK                  |jM                                      j@                         jB                  dk  r0 jN                  jK                  |jM                         |             |j>                        d }~wjP                  $ r5} jN                  jK                  |jM                         |            d }~ww xY w)Nr   _lua_runtimeT)encodingunpack_returned_tuples
z = require('z')aA  
                function(redis_call, redis_pcall, redis_log, cjson_encode, cjson_decode, cjson_null)
                    redis = {}
                    redis.call = redis_call
                    redis.pcall = redis_pcall
                    redis.log = redis_log
                    redis.LOG_DEBUG = 0
                    redis.LOG_VERBOSE = 1
                    redis.LOG_NOTICE = 2
                    redis.LOG_WARNING = 3
                    redis.error_reply = function(msg) return {err=msg} end
                    redis.status_reply = function(msg) return {ok=msg} end

                    cjson = {}
                    cjson.encode = cjson_encode
                    cjson.decode = cjson_decode
                    cjson.null = cjson_null

                    KEYS = {}
                    ARGV = {}
                    z%
                end
                z
                function(keys, argv)
                    KEYS = keys
                    ARGV = argv
                end
                c                       y r   r   argss    r   rA   zD_patch_fakeredis_lua_runtime.<locals>.patched_eval.<locals>.<lambda>      r"   c                       y r   r   r   s    r   rA   zD_patch_fakeredis_lua_runtime.<locals>.patched_eval.<locals>.<lambda>  r   r"   c                       y r   r   r   s    r   rA   zD_patch_fakeredis_lua_runtime.<locals>.patched_eval.<locals>.<lambda>  r   r"   c                       y r   r   r   s    r   rA   zD_patch_fakeredis_lua_runtime.<locals>.patched_eval.<locals>.<lambda>  r   r"   c                       y r   r   r   s    r   rA   zD_patch_fakeredis_lua_runtime.<locals>.patched_eval.<locals>.<lambda>  r   r"   r   .c                      j                   dt        dt        j                  dt        j                  ffd} | S )Nopr   r   c                 L    j                   d   } |j                  | g| S Nr   )_lua_current_socket_lua_redis_callr   r   socketeglrry   s      r   wrapperzd_patch_fakeredis_lua_runtime.<locals>.patched_eval.<locals>.make_redis_call_wrapper.<locals>.wrapper  s/    #77:F1611"b"DtDDr"   _serverbytestypingAnyr   r   r   ry   expected_globalslua_runtimer   s    @@@r   make_redis_call_wrapperzS_patch_fakeredis_lua_runtime.<locals>.patched_eval.<locals>.make_redis_call_wrapper  sC     %E Efjj EVZZ E r"   c                      j                   dt        dt        j                  dt        j                  ffd} | S )Nr   r   r   c                 L    j                   d   } |j                  | g| S r   )r   _lua_redis_pcallr   s      r   r   ze_patch_fakeredis_lua_runtime.<locals>.patched_eval.<locals>.make_redis_pcall_wrapper.<locals>.wrapper  s/    #77:F26222r2EEEr"   r   r   s    @@@r   make_redis_pcall_wrapperzT_patch_fakeredis_lua_runtime.<locals>.patched_eval.<locals>.make_redis_pcall_wrapper  sC     %F Ffjj FVZZ F r"   )   valkeyzcollectgarbage()F)nested)*lenTOO_MANY_KEYS_MSGNEGATIVE_KEYS_MSGsha1	hexdigestencoder   script_cachehasattr
LuaRuntimer   joinload_lua_moduleseval_lua_set_keys_argvsetglobalskeys_lua_expected_globalsr   r   Callabler   _lua_redis_call_wrapper_lua_redis_pcall_wrapperpartial_lua_log_partial_lua_cjson_encode_partial_lua_cjson_decode_partial
table_fromexecutevalueLUA_COMMAND_ARG_MSGversionLUA_COMMAND_ARG_MSG6server_typeVALKEY_LUA_COMMAND_ARG_MSGformatdecodeSCRIPT_ERROR_MSGLuaError_convert_lua_result)r   r   r   r   r   modulemodules_import_strset_globals_initr   r   resultexr   r   r   r   r   r   r   r   r   	functoolshashlibmsgss   `           @@r   patched_evalz2_patch_fakeredis_lua_runtime.<locals>.patched_eval  s\    S''d4455Q;d4455||F#--/668*0!!$' t||^4(2(=(=d )> )DLL% ,,33K!%AEAVAVWvF8<xr2W"
  +//( (( )) 6 /:.>.>/DLL+ """"" 25[5H5H5J5O5O5Q1RDLL.#||AA 15vDLL,	V__S&**_-M 		fooc6::o.N 	 4K3LDLL04L4NDLL1,5,=,=-=-DLL) 6?5F5F!;0@6DLL2 6?5F5F!;0@6DLL2
 4455--6666 ll//<<== /3((+ 	''""='#:;""=#:;	

	O ((0F" 	{,<= 	./''u'==Q Xf  	(xx4333<<$&%d&?&?@@\\--9%77>>t{{}M  &d&>&>??||d"!$"7"7">">t{{}b"QRRbhh''"" 	Od33::4;;="MNN	Os+   *OO S8C%R11S80S33S8)_lua_patch_appliedr   r   	fakeredisr   fakeredis._commandsr   r   fakeredis._helpersr   )fakeredis.commands_mixins.scripting_mixinr   r   r   r   r   r   r   ImportErrorr   FLAG_NO_SCRIPTrr   r   r   r   )r   r   r   r   r   r   r   r   r   r   r   r   r   r   s       @@@@@@@@@@r   r{   r{   e  s    '0. H eS\E84+>+>?]>$]>]> ]> 	]>
 
]> ]> @]>~ #/G  s   B% %	B10B1r   )-r!   asynciologgingr   
contextlibr   r   typesr   r   r   urllib.parser   r	   r
   redis.asyncior   r   redis.asyncio.clientr   redis.asyncio.clusterr   redis.asyncio.connectionr   r   TYPE_CHECKINGrz   r   	getLoggerr   r   Loggerr   r   r   r*   r+   dictLockr-   r/   r3   r5   r   r{   r   r"   r   <module>r
     s       :  + : : / ' . >	- +**84 4(X (B> B B B ,.c<'( -#w||~  $3 $#6 $NP NPx  {/r"   