
    ng\j                    l   S r SSKJr  SSKrSSKrSSKrSSKrSSKJrJ	r	  SSK
JrJrJr  SSKJr  SSKJrJr  SSKJrJrJr  SS	KJrJrJr  SS
KJrJrJrJrJ r J!r!J"r"J#r#J$r$  SSK%J&r&  \RN                  " \(5      r)\\/S\S   -  4   r* " S S\5      r+ " S S\+5      r, " S S\,5      r- " S S\,5      r.g)a  
Async Redis Keyspace Notifications support for redis-py.

This module provides async utilities for subscribing to and parsing Redis
keyspace notifications.

Standalone Redis Example:
    >>> from redis.asyncio import Redis
    >>> from redis.asyncio.keyspace_notifications import (
    ...     AsyncKeyspaceNotifications,
    ... )
    >>> from redis.keyspace_notifications import KeyspaceChannel, EventType
    >>>
    >>> async def main():
    ...     async with Redis() as r:
    ...         async with AsyncKeyspaceNotifications(r) as ksn:
    ...             channel = KeyspaceChannel("user:*")
    ...             await ksn.subscribe(channel)
    ...             async for notification in ksn.listen():
    ...                 print(f"Key: {notification.key}, Event: {notification.event_type}")

Redis Cluster Example:
    >>> from redis.asyncio.cluster import RedisCluster
    >>> from redis.asyncio.keyspace_notifications import (
    ...     AsyncClusterKeyspaceNotifications,
    ... )
    >>> from redis.keyspace_notifications import KeyspaceChannel, EventType
    >>>
    >>> async def main():
    ...     async with RedisCluster(host="localhost", port=7000) as rc:
    ...         async with AsyncClusterKeyspaceNotifications(rc) as ksn:
    ...             channel = KeyspaceChannel("user:*")
    ...             await ksn.subscribe(channel)
    ...             async for notification in ksn.listen():
    ...                 print(f"Key: {notification.key}, Event: {notification.event_type}")
    )annotationsN)ABCabstractmethod)AsyncIterator	AwaitableCallable)Any)PubSubRedis)ClusterNodeRedisCluster_ClusterNodePoolAdapter)ConnectionError
RedisErrorTimeoutError)	ChannelTKeyeventChannelKeyNotificationKeyspaceChannelSubkeyeventChannelSubkeyspaceChannelSubkeyspaceeventChannelSubkeyspaceitemChannel_is_pattern)safe_strc                     \ rS rSrSr\SS.   SS jj5       r\SS j5       r\  S     SS jj5       r\  S     SS jj5       r	\  S     SS	 jj5       r
\  S     SS
 jj5       r\  S       SS jj5       r\  S       SS jj5       r\  S     SS jj5       r\SS j5       r\S 5       r\S 5       r\S 5       r\\SS j5       5       r\  S      S!S jj5       rSrg)"#AsyncKeyspaceNotificationsInterfaceP   z
Async interface for keyspace notification managers.

This interface provides a consistent async API for both standalone
(AsyncKeyspaceNotifications) and cluster (AsyncClusterKeyspaceNotifications)
implementations.
Nhandlerc                  #    g7f)z,Subscribe to keyspace notification channels.N )selfr    channelss      _/home/edenadmin/noVNC/venv/lib/python3.13/site-packages/redis/asyncio/keyspace_notifications.py	subscribe-AsyncKeyspaceNotificationsInterface.subscribeY   
      	   c                   #    g7f)0Unsubscribe from keyspace notification channels.Nr"   )r#   r$   s     r%   unsubscribe/AsyncKeyspaceNotificationsInterface.unsubscribeb   
      	r)   c                   #    g7f)6Subscribe to keyspace notifications for specific keys.Nr"   r#   key_or_patterndbr    s       r%   subscribe_keyspace6AsyncKeyspaceNotificationsInterface.subscribe_keyspaceg   
      	r)   c                   #    g7f)=Subscribe to keyevent notifications for specific event types.Nr"   r#   eventr3   r    s       r%   subscribe_keyevent6AsyncKeyspaceNotificationsInterface.subscribe_keyeventq   r6   r)   c                   #    g7f)9Subscribe to subkeyspace notifications for specific keys.Nr"   r1   s       r%   subscribe_subkeyspace9AsyncKeyspaceNotificationsInterface.subscribe_subkeyspace{   r6   r)   c                   #    g7f)@Subscribe to subkeyevent notifications for specific event types.Nr"   r9   s       r%   subscribe_subkeyevent9AsyncKeyspaceNotificationsInterface.subscribe_subkeyevent   r6   r)   c                   #    g7f)ASubscribe to subkeyspaceitem notifications for a specific subkey.Nr"   )r#   r2   subkey_or_patternr3   r    s        r%   subscribe_subkeyspaceitem=AsyncKeyspaceNotificationsInterface.subscribe_subkeyspaceitem   
      	r)   c                   #    g7f)BSubscribe to subkeyspaceevent notifications for an event on a key.Nr"   )r#   r:   r2   r3   r    s        r%   subscribe_subkeyspaceevent>AsyncKeyspaceNotificationsInterface.subscribe_subkeyspaceevent   rJ   r)   c                   #    g7f)z7Get the next keyspace notification if one is available.Nr"   )r#   ignore_subscribe_messagestimeouts      r%   get_message/AsyncKeyspaceNotificationsInterface.get_message   r(   r)   c                    g)z"Listen for keyspace notifications.Nr"   r#   s    r%   listen*AsyncKeyspaceNotificationsInterface.listen   s     	    c                   #    g7f)z6Close the notification manager and clean up resources.Nr"   rU   s    r%   aclose*AsyncKeyspaceNotificationsInterface.aclose   r.   r)   c                   #    g 7fNr"   rU   s    r%   
__aenter__.AsyncKeyspaceNotificationsInterface.__aenter__        r)   c                   #    g 7fr]   r"   r#   	_exc_type_exc_val_exc_tbs       r%   	__aexit__-AsyncKeyspaceNotificationsInterface.__aexit__   r`   r)   c                    g);Check if there are any active subscriptions and not closed.Nr"   rU   s    r%   
subscribed.AsyncKeyspaceNotificationsInterface.subscribed   s     	rX   c                   #    g7f)z
Run the notification loop as a coroutine.

This is the async equivalent of run_in_thread() for sync notifications.
Use asyncio.create_task() to run in the background.

The exception_handler can be either a sync or async function.
Nr"   )r#   poll_timeoutexception_handlers      r%   run'AsyncKeyspaceNotificationsInterface.run   s
     $ 	r)   r"   r$   r   r    AsyncHandlerT | Noner$   r   r   Nr2   strr3   intr    rr   r:   rv   r3   rw   r    rr   r2   rv   rG   rv   r3   rw   r    rr   r:   rv   r2   rv   r3   rw   r    rr   N        rP   zbool | NonerQ   floatreturnKeyNotification | Noner   zAsyncIterator[KeyNotification]r   bool      ?Nrm   r~   rn   z]Callable[[BaseException, AsyncKeyspaceNotificationsInterface], None | Awaitable[None]] | Noner   None)__name__
__module____qualname____firstlineno____doc__r   r&   r,   r4   r;   r?   rC   rH   rM   rR   rV   rZ   r^   rf   propertyrj   ro   __static_attributes__r"   rX   r%   r   r   P   s     )- &     (,	  &	   (,	  &	   (,	  &	   (,	  &	  
 (,  	
 &  
 (,  	
 &   26#.  
 	              "
  
 rX   r   c                     \ rS rSrSr  S   SS jjrSS.   SS jjr\      SS j5       rSS jr	\      SS	 j5       r
      SS
 jr      SS jr  S     SS jjr  S     SS jjr  S     SS jjr  S     SS jjr  S       SS jjr  S       S S jjrS rS r  S!     S"S jjrSrg)#"AbstractAsyncKeyspaceNotifications   a}  
Abstract base class for async keyspace notification managers.

Provides shared implementation for subscribe/unsubscribe logic.
Subclasses must implement:
- _execute_subscribe: Execute the subscribe operation
- _execute_unsubscribe: Execute the unsubscribe operation
- get_message: Get the next notification
- listen: Async generator for notifications
- aclose: Clean up resources
Nc                *    Xl         X l        SU l        g)a!  
Initialize the base async keyspace notification manager.

Args:
    key_prefix: Optional prefix to filter and strip from keys in notifications
    ignore_subscribe_messages: If True, subscribe/unsubscribe confirmations
                              are not returned by get_message/listen
FN)
key_prefixrP   _closed)r#   r   rP   s      r%   __init__+AbstractAsyncKeyspaceNotifications.__init__   s     %)B&rX   r   c                 ^^^#    SnTb>  U R                   m[        R                  " T5      nU(       a  TmUU4S jnUnO	UU4S jnUn0 n0 nU HE  n	[        U	S5      (       a  [	        U	5      n
O[        U	5      n
[        U	5      (       a  X7U
'   MA  X8U
'   MG     U R                  Xx5      I Sh  vN   U R                  Xx5        g N7f)u  
Subscribe to keyspace notification channels.

Automatically detects whether each channel is a pattern (contains
wildcards like *, ?, [) or an exact channel name and uses the
appropriate Redis subscribe command internally.

The handler can be either a sync or async function.  Note that a
**sync** handler will be called directly on the event loop thread,
so it must not perform blocking I/O or long-running computation —
prefer an ``async`` handler whenever possible.
Nc                d   >#    [         R                  " U TS9nUb  T" U5      I S h  vN   g g  N7fNr   r   from_message)messagenotificationasync_handlerr   s     r%   _async_wrap_handlerIAbstractAsyncKeyspaceNotifications.subscribe.<locals>._async_wrap_handler  s9     #2#?#?J$L $/+L999 09s   $0.0c                H   > [         R                  " U TS9nUb	  T" U5        g g r   r   )r   r   r    r   s     r%   _sync_wrap_handlerHAbstractAsyncKeyspaceNotifications.subscribe.<locals>._sync_wrap_handler#  s-    #2#?#?J$L $/- 0rX   _channel_str)	r   inspectiscoroutinefunctionhasattrrv   r   r   _execute_subscribe_track_subscribe)r#   r    r$   wrapped_handleris_async_handlerr   r   patternsexact_channelschannelchannel_strr   r   s    `         @@r%   r&   ,AbstractAsyncKeyspaceNotifications.subscribe   s     $ ,0J&::7C ': #6. #5Gw//!'l&w/7##(7%.={+   %%h???h7 	@s   B'C,C-Cc                   #    g7f)z Execute the subscribe operation.Nr"   r#   r   r   s      r%   r   5AbstractAsyncKeyspaceNotifications._execute_subscribe@  
     
 	r)   c                6  #    / n/ nU H_  n[        US5      (       a  [        U5      nO[        U5      n[        U5      (       a  UR	                  U5        MN  UR	                  U5        Ma     U R                  X#5      I Sh  vN   U R                  X#5        g N7f)r+   r   N)r   rv   r   r   append_execute_unsubscribe_untrack_subscribe)r#   r$   r   r   r   r   s         r%   r,   .AbstractAsyncKeyspaceNotifications.unsubscribeG  s     Gw//!'l&w/7##,%%k2    ''AAA9 	Bs   A>B BBc                   #    g7f)z"Execute the unsubscribe operation.Nr"   r   s      r%   r   7AbstractAsyncKeyspaceNotifications._execute_unsubscribe_  r   r)   c                    g)a3  Track newly subscribed patterns/channels.

Override in subclasses that need to maintain their own subscription
registry (e.g. cluster implementations that must re-subscribe
new/failed-over nodes).  The default is a no-op because standalone
implementations delegate tracking to the underlying PubSub object.
Nr"   r   s      r%   r   3AbstractAsyncKeyspaceNotifications._track_subscribef      rX   c                    g)zRemove patterns/channels from the subscription registry.

Override in subclasses that maintain their own subscription registry.
The default is a no-op.
Nr"   r   s      r%   r   5AbstractAsyncKeyspaceNotifications._untrack_subscribeq  r   rX   c                P   #    [        XS9nU R                  XCS9I Sh  vN   g N7f)r0   r3   r   N)r   r&   r#   r2   r3   r    r   s        r%   r4   5AbstractAsyncKeyspaceNotifications.subscribe_keyspacez  s%      ".8nnWn666   &$&c                P   #    [        XS9nU R                  XCS9I Sh  vN   g N7f)r8   r   r   N)r   r&   r#   r:   r3   r    r   s        r%   r;   5AbstractAsyncKeyspaceNotifications.subscribe_keyevent  s%      "%/nnWn666r   c                P   #    [        XS9nU R                  XCS9I Sh  vN   g N7f)r>   r   r   N)r   r&   r   s        r%   r?   8AbstractAsyncKeyspaceNotifications.subscribe_subkeyspace  s%      %^;nnWn666r   c                P   #    [        XS9nU R                  XCS9I Sh  vN   g N7f)rB   r   r   N)r   r&   r   s        r%   rC   8AbstractAsyncKeyspaceNotifications.subscribe_subkeyevent  s%      %U2nnWn666r   c                R   #    [        XUS9nU R                  XTS9I Sh  vN   g N7f)rF   r   r   N)r   r&   )r#   r2   rG   r3   r    r   s         r%   rH   <AbstractAsyncKeyspaceNotifications.subscribe_subkeyspaceitem  s'      )rRnnWn666   '%'c                R   #    [        XUS9nU R                  XTS9I Sh  vN   g N7f)rL   r   r   N)r   r&   )r#   r:   r2   r3   r    r   s         r%   rM   =AbstractAsyncKeyspaceNotifications.subscribe_subkeyspaceevent  s'      *%BGnnWn666r   c                   #    U $ 7fr]   r"   rU   s    r%   r^   -AbstractAsyncKeyspaceNotifications.__aenter__  s
     s   c                @   #    U R                  5       I S h  vN   g N7f)NF)rZ   rb   s       r%   rf   ,AbstractAsyncKeyspaceNotifications.__aexit__  s     kkm 	s   c                D  #    U R                   (       a,   U R                  US9I Sh  vN   U R                   (       a  M+  gg N! [        R                   a    e [         a<  nUb3  U" X05      n[
        R                  " U5      (       a  UI Sh  vN     SnANke SnAff = f7f)aV  
Run the notification loop as a coroutine.

This continuously polls for notifications and triggers handlers.
Use asyncio.create_task() to run in the background.

Args:
    poll_timeout: Timeout in seconds for each get_message call.
    exception_handler: Optional callback for handling exceptions.
                      Can be sync or async.
rQ   N)rj   rR   asyncioCancelledErrorBaseExceptionr   isawaitable)r#   rm   rn   eresults        r%   ro   &AbstractAsyncKeyspaceNotifications.run  s     ( oo
&&|&<<< ooo<))   $0.q7F**622$sT   B A AA B B A B!+BBBB BBB )r   rP   r   NT)r   str | bytes | NonerP   r   rq   r   dict[str, Any]r   r   r   r   rs   r   	list[str]r   r   r   r   rt   ru   rx   ry   rz   r   r   )r   r   r   r   r   r   r&   r   r   r,   r   r   r   r4   r;   r?   rC   rH   rM   r^   rf   ro   r   r"   rX   r%   r   r      s!   
 *.*.& $(( )-@8@8 &@8D &8F	 :0 !3<	 	&	8F			!3<	 (,	77 7 &	7 (,	77 7 &	7 (,	77 7 &	7 (,	77 7 &	7 (,	7	7 	7 		7
 &	7 (,	7	7 	7 		7
 &	7 "
  
 rX   r   c                     ^  \ rS rSrSr  S     SU 4S jjjr      SS jr      SS jr  S     SS jjrSS jr	\
SS j5       rS	 rS
rU =r$ )AsyncKeyspaceNotificationsi  a  
Manages keyspace notification subscriptions for standalone async Redis.

For standalone Redis, keyspace notifications work with a single PubSub
connection. This class wraps that connection and provides:
- Automatic pattern vs exact channel detection
- KeyNotification parsing with optional key_prefix filtering
- Convenience methods for keyspace and keyevent subscriptions
- Context manager and run() coroutine support
c                X   > [         TU ]  X#5        Xl        UR                  SS9U l        g)a  
Initialize the standalone async keyspace notification manager.

Note: Keyspace notifications must be enabled on the Redis server via
the ``notify-keyspace-events`` configuration option.

Args:
    redis_client: An async Redis client instance
    key_prefix: Optional prefix to filter and strip from keys in notifications
    ignore_subscribe_messages: If True, subscribe/unsubscribe confirmations
                              are not returned by get_message/listen
F)rP   N)superr   redispubsub_pubsub)r#   redis_clientr   rP   	__class__s       r%   r   #AsyncKeyspaceNotifications.__init__  s,    $ 	?!
+22U2SrX   c                   #    U(       a$  U R                   R                  " S0 UD6I Sh  vN   U(       a%  U R                   R                  " S0 UD6I Sh  vN   gg N1 N7f)z2Execute subscribe on the single pubsub connection.Nr"   )r   
psubscriber&   r   s      r%   r   -AsyncKeyspaceNotifications._execute_subscribe	  sP      ,,))5H555,,((:>:::  6:s!   'AA*AAAAc                   #    U(       a!  U R                   R                  " U6 I Sh  vN   U(       a"  U R                   R                  " U6 I Sh  vN   gg N. N7f)z4Execute unsubscribe on the single pubsub connection.N)r   punsubscriber,   r   s      r%   r   /AsyncKeyspaceNotifications._execute_unsubscribe  sJ      ,,++X666,,**N;;;  7;s!   $AA'AAAAc                   #    Uc  U R                   nU R                  (       a  gU R                  R                  UUS9I Sh  vN nUb  [        R
                  " X0R                  S9$ g N&7f)a  
Get the next keyspace notification if one is available.

Args:
    ignore_subscribe_messages: If True, skip subscribe/unsubscribe messages.
                              Defaults to the value set in __init__ (True).
    timeout: Time to wait for a message.

Returns:
    A KeyNotification if a notification is available and no handler
    was registered for the channel, None otherwise.
NrP   rQ   r   )rP   r   r   rR   r   r   r   )r#   rP   rQ   r   s       r%   rR   &AsyncKeyspaceNotifications.get_message  sl     " %,(,(F(F%<<00&? 1 
 

 "//OOTT
s   ?A*A('A*c                  #    U R                   (       a3  U R                  SS9I Sh  vN nUb  U7v   U R                   (       a  M2  gg N!7f)a@  
Listen for keyspace notifications.

This is an async generator that yields KeyNotification objects as they arrive.

Yields:
    KeyNotification objects for each keyspace/keyevent notification.

Example:
    >>> async for notification in ksn.listen():
    ...     print(f"{notification.key}: {notification.event_type}")
r   r   Nrj   rR   r#   r   s     r%   rV   !AsyncKeyspaceNotifications.listen<  >      oo!%!1!1#!1!>>L'"" ooo>   $A
AA
A
c                ^    U R                   (       + =(       a    U R                  R                  $ ri   )r   r   rj   rU   s    r%   rj   %AsyncKeyspaceNotifications.subscribedN  s      <<;DLL$;$;;rX   c                   #    SU l          U R                  R                  5       I Sh  vN   g N! [         a     gf = f7f)z3Close the pubsub connection and clean up resources.TN)r   r   rZ   	ExceptionrU   s    r%   rZ   !AsyncKeyspaceNotifications.acloseS  s8     	,,%%''' 		s,   A 0 .0 A 0 
=A =A )r   r   r   r   )r   r   r   r   rP   r   r   r   r{   r}   r   r   )r   r   r   r   r   r   r   r   rR   rV   r   rj   rZ   r   __classcell__r   s   @r%   r   r     s    	 *.*.	TT 'T $(	T T.;&;8F;	;<!<3<<	< 26#.  
 	B#$ < < rX   r   c                  2  ^  \ rS rSrSr  S     SU 4S jjjr\SS j5       r      SS jr      SS jr	SS jr
SS jrSS	 jr      SS
 jr      SS jr    SS jr  S     SS jjr    S S jrS!S jrS rSS jrS rS rSrU =r$ )"!AsyncClusterKeyspaceNotificationsia  a?  
Manages keyspace notification subscriptions across all nodes in an async Redis Cluster.

In Redis Cluster, keyspace notifications are NOT broadcast between nodes.
Each node only emits notifications for keys it owns. This class automatically
subscribes to all primary nodes in the cluster and handles topology changes.
c                   > [         TU ]  X#5        Xl        0 U l        0 U l        0 U l        [        R                  " 5       U l        SU l	        g)a  
Initialize the async cluster keyspace notification manager.

Note: Keyspace notifications must be enabled on all Redis cluster nodes via
the ``notify-keyspace-events`` configuration option.

Args:
    redis_cluster: An async RedisCluster instance
    key_prefix: Optional prefix to filter and strip from keys in notifications
    ignore_subscribe_messages: If True, subscribe/unsubscribe confirmations
                              are not returned by get_message/listen
r   N)
r   r   cluster_subscribed_patterns_subscribed_channels_node_pubsubsr   Lock_refresh_lock_poll_index)r#   redis_clusterr   rP   r   s       r%   r   *AsyncClusterKeyspaceNotifications.__init__j  sM    $ 	?$ 57!46! 13 %\\^ rX   c                    U R                   (       + =(       a(    [        U R                  =(       d    U R                  5      $ r   )r   r   r  r  rU   s    r%   rj   ,AsyncClusterKeyspaceNotifications.subscribed  s3     << 
D%%B)B)B%
 	
rX   c                    U(       a  U R                   R                  U5        U(       a  U R                  R                  U5        gg)zATrack newly subscribed patterns/channels in the cluster registry.N)r  updater  r   s      r%   r   2AsyncClusterKeyspaceNotifications._track_subscribe  s7     %%,,X6%%,,^< rX   c                    U H  nU R                   R                  US5        M!     U H  nU R                  R                  US5        M!     g)z3Remove patterns/channels from the cluster registry.N)r  popr  )r#   r   r   pcs        r%   r   4AsyncClusterKeyspaceNotifications._untrack_subscribe  sD     A%%))!T2 A%%))!T2  rX   c                6    U R                   R                  5       $ )z%Get all primary nodes in the cluster.)r
  get_primariesrU   s    r%   _get_all_primary_nodes8AsyncClusterKeyspaceNotifications._get_all_primary_nodes  s    ||))++rX   c                   #    UR                   U R                  ;  a-  [        U5      n[        USS9nX0R                  UR                   '   U R                  UR                      $ 7f)a  Get or create a PubSub instance for a node.

Uses a :class:`_ClusterNodePoolAdapter` to borrow a connection
from the node's existing pool.  When the ``PubSub`` is closed
the connection is disconnected and returned to the node,
ensuring no subscribed socket is left in the free queue.
F)connection_poolrP   )namer  r   r
   )r#   nodepool_adapterr   s       r%   _ensure_node_pubsub5AsyncClusterKeyspaceNotifications._ensure_node_pubsub  s\      99D...248L ,*/F -3tyy)!!$)),,s   A!A#c                   #    U R                   R                  US5      nU(       a   UR                  5       I Sh  vN   gg N! [         a     gf = f7f)zRemove and close a node's PubSub.

``PubSub.aclose()`` disconnects the connection and releases it
back to the underlying :class:`ClusterNode` via the adapter.
N)r  r  rZ   r  )r#   	node_namer   s      r%   _cleanup_node/AsyncClusterKeyspaceNotifications._cleanup_node  sO      ##''	48mmo%% % s7   $AA AA AA 
AAAAc                <  #    U(       d  U(       d  g/ nU R                  5        H  nUR                  U R                  ;  nU R                  U5      I Sh  vN n U(       aj  U R                  (       a$  UR
                  " S0 U R                  D6I Sh  vN   U R                  (       a$  UR                  " S0 U R                  D6I Sh  vN   U(       a  UR
                  " S0 UD6I Sh  vN   U(       a  UR                  " S0 UD6I Sh  vN   M  M     U(       a&  [        R                  SSR                  U5      5        gg N N N~ N_ N@! [         aD    U R                  UR                  5      I Sh  vN    UR                  UR                  5         GMt  f = f7f)aG  Execute subscribe on all cluster nodes.

Patterns and exact channels are subscribed in a single pass over
nodes so that a mid-batch node failure cannot create a
partially-caught-up replacement.  If a node fails during this
call it is removed from ``_node_pubsubs`` and will be fully
re-subscribed on the next ``refresh_subscriptions`` cycle.

If a newly discovered node is encountered (not yet in
``_node_pubsubs``), it is also subscribed to all *previously*
tracked patterns/channels so it doesn't miss notifications for
subscriptions that were established before this node joined.
Nz`Failed to subscribe on cluster nodes: %s. These nodes will be retried on the next refresh cycle., r"   )r  r#  r  r&  r  r   r  r&   r  r*  r   loggerwarningjoin)r#   r   r   failed_nodesr$  is_new_noder   s          r%   r   4AsyncClusterKeyspaceNotifications._execute_subscribe  sX      "$//1D))4+=+==K33D99F/ 00$//L$2K2KLLL00$..K1J1JKKK ++7h777! **<^<<< " 2, NNI		,' ) : MK 8< / ((333##DII..	/s   AFEF7EE4EE E'E( EE		E5FEEE	E(F3E64 FFFFc                   #    U(       a  U R                  USS9I Sh  vN   U(       a  U R                  USS9I Sh  vN   gg N% N7f)z)Execute unsubscribe on all cluster nodes.T)use_punsubscribeNF)_unsubscribe_from_all_nodesr   s      r%   r   6AsyncClusterKeyspaceNotifications._execute_unsubscribe  sX      228d2SSS22 3     Ts   AAAAAAc                  #    / n[        U R                  R                  5       5       H[  nU R                  R                  U5      nUc  M#   U(       a  UR                  " U6 I Sh  vN   MD  UR
                  " U6 I Sh  vN   M]     U(       a&  [        R                  SSR                  U5      5        gg NO N8! [         a/    U R                  U5      I Sh  vN    UR                  U5         M  f = f7f)a]  Unsubscribe from patterns/channels on all nodes.

Best-effort: tries every node so that a single broken connection
does not prevent the remaining nodes from being unsubscribed.
Broken pubsubs are cleaned up; the tracking state is still removed
by the caller, so ``refresh_subscriptions`` will *not* re-subscribe
these channels on replacement nodes.
NzeFailed to unsubscribe on cluster nodes: %s. These nodes will be re-created on the next refresh cycle.r-  )listr  keysgetr   r,   r  r*  r   r.  r/  r0  )r#   r$   r5  r1  r)  r   s         r%   r6  =AsyncClusterKeyspaceNotifications._unsubscribe_from_all_nodes  s      #%d005578I''++I6F~/# --x888 ,,h777 9 NNL		,'  97 /((333##I./sf   A	C5B9%B5&B9*C5,B9>B7?B92C55B97B99C2CC2.C51C22C5c                x  #    U R                   (       a  g[        U R                  5      nUS:X  a%  US:  a  [        R                  " U5      I Sh  vN   gUc  U R
                  nUS:X  a  U R                  U5      I Sh  vN $ [        SU[        US5      -  5      n[        R                  " 5       nXR-   n [        R                  " 5       U:  a  g[        U R                  R                  5       5      nU(       d  gU R                  [        U5      -  U l        XpR                     nU =R                  S-  sl         UR                  UUS9I Sh  vN n	U	b#  [&        R(                  " XR*                  S9n
U
b  U
$ M   GN- GN N2! [        [         ["        4 a    U R%                  5       I Sh  vN     GM  f = f7f)a.  
Get the next keyspace notification if one is available.

This method polls all node pubsubs in round-robin fashion until
a message is received or the timeout expires.
If a connection error occurs, subscriptions are automatically refreshed.

Args:
    ignore_subscribe_messages: If True, skip subscribe/unsubscribe messages.
                              Defaults to the value set in __init__ (True).
    timeout: Total time to wait for a message (distributed across all nodes)

Returns:
    A KeyNotification if a notification is available, None otherwise.
Nr   r|   g?   r   r   )r   lenr  r   sleeprP   _poll_all_nodes_onceminmaxtime	monotonicr9  valuesr  rR   r   r   r   _refresh_subscriptions_on_errorr   r   r   )r#   rP   rQ   total_nodesper_node_timeout
start_timeend_timepubsubsr   r   r   s              r%   rR   -AsyncClusterKeyspaceNotifications.get_message&  s    ( <<$,,-! {mmG,,,$,(,(F(F% c>223LMMM sGc+q.A$AB^^%
'~~8+4--4467G  $//#g,>D--.F! & 2 2.G, !3 !  ".;;   +''7 ! - N, $\:> ::<<<sg   AF:E<.F:>E??B<F:<F FF )F:?F:F (F7,F/-F72F:6F77F:c                  #    Sn[        U R                  R                  5       5       He  n UR                  USS9I Sh  vN nUc  M!  [        R                  " X@R                  S9nUc  MD  U(       a  U R                  5       I Sh  vN   Us  $    U(       a  U R                  5       I Sh  vN   g Nq! [        [
        [        4 a    Sn M  f = f NJ N'7f)z
Perform a single non-blocking poll over all node pubsubs.

Returns:
    A KeyNotification if one is available, None otherwise.
Fr|   r   NTr   )r9  r  rF  rR   r   r   r   r   r   r   rG  )r#   rP   	had_errorr   r   r   s         r%   rA  6AsyncClusterKeyspaceNotifications._poll_all_nodes_oncep  s      	4--4467F	 & 2 2.G !3 !  ".;;   + !"BBDDD''+ 80 668881 $\:>  !		 E
 9sc   )CB3B1 B3C	C,CC$C+C,C1B33C
CCCCc                  #    U R                   (       a3  U R                  SS9I Sh  vN nUb  U7v   U R                   (       a  M2  gg N!7f)aW  
Listen for keyspace notifications from all cluster nodes.

This is an async generator that yields KeyNotification objects as they arrive.

Yields:
    KeyNotification objects for each keyspace/keyevent notification.

Example:
    >>> async for notification in ksn.listen():
    ...     print(f"{notification.key}: {notification.event_type}")
r   r   Nr   r   s     r%   rV   (AsyncClusterKeyspaceNotifications.listen  r   r   c                   #    SU l          U R                  5       I Sh  vN   g N! [         a    [        R	                  SSS9   gf = f7f)z
Refresh subscriptions after a connection error.

This is called automatically when a connection error occurs during
get_message(). It checks if nodes changed before refreshing.
r   NzAFailed to refresh cluster subscriptions, will retry on next errorT)exc_info)r  refresh_subscriptionsr  r.  r/  rU   s    r%   rG  AAsyncClusterKeyspaceNotifications._refresh_subscriptions_on_error  sK      	,,... 	NNS  	s1   A
& $& A
& AA
AA
c                \     UR                   nUc  gUR                  $ ! [         a     gf = f)z,Check if a pubsub connection is still alive.F)
connectionis_connectedr  )r#   r   conns      r%   _is_pubsub_connected6AsyncClusterKeyspaceNotifications._is_pubsub_connected  s8    	$$D|$$$ 		s     
++c                d  #    U R                    ISh  vN   U R                  5        Vs0 s H  oR                  U_M     nn[        U R                  R                  5       5      [        UR                  5       5      -
  nU H  nU R                  U5      I Sh  vN   M     [        U R                  R                  5       5      [        UR                  5       5      -  nU HX  nU R                  R                  U5      nU(       d  M'  U R                  U5      (       a  M?  U R                  U5      I Sh  vN   MZ     [        UR                  5       5      [        U R                  R                  5       5      -
  n/ nU H  nX$   nU R                  U5      I Sh  vN n U R                  (       a$  UR                  " S0 U R                  D6I Sh  vN   U R                  (       a&  UR                  " S0 U R                  D6I Sh  vN   M  M     U(       a  [!        SSR#                  U5       35      eSSS5      ISh  vN   g GN0s  snf  GN GN N N NQ! [         a0    U R                  U5      I Sh  vN    UR                  U5         GM  f = f NV! , ISh  vN  (       d  f       g= f7f)a|  
Refresh subscriptions after a topology change.

This method is called automatically when topology changes are detected
or when connection errors occur. You can also call it manually if needed.

This method:
1. Discovers any new primary nodes and subscribes them
2. Removes pubsubs for nodes that are no longer primaries
3. Re-creates broken pubsub connections for existing nodes
Nz&Failed to subscribe to cluster nodes: r-  r"   )r  r  r#  setr  r:  r*  r;  r[  r&  r  r   r  r&   r  r   r   r0  )	r#   r$  current_primariesremoved_nodesr)  existing_nodesr   	new_nodesr1  s	            r%   rU  7AsyncClusterKeyspaceNotifications.refresh_subscriptions  sQ     %%%,0,G,G,I!,ID		4,I  !
   2 2 7 7 9:S!&&(> M +	((333 +
 !!3!3!8!8!:;c!&&(? N ,	++//	:6$";";F"C"C,,Y777	 , -2245D<N<N<S<S<U8VVI&(L&	(3#77==300$//L$2K2KLLL00$..K1J1JKKK 1 ' %<TYY|=T<UV W &%%! 4 8 > MK  3,,Y777 ''	223I &%%%s   J0IJ0JI AJIA*JJJ3I4A$JIJ0II4III*J2J0=J>J0JJJIIJ5I8
6JJJJJ0J-JJ-)J0c                  #    SU l         [        U R                  R                  5       5       H  nU R	                  U5      I Sh  vN   M     U R
                  R                  5         U R                  R                  5         g N=7f)z4Close all pubsub connections and clean up resources.TN)r   r9  r  r:  r*  r  clearr  )r#   r)  s     r%   rZ   (AsyncClusterKeyspaceNotifications.aclose  sg     d005578I$$Y/// 9!!'')!!'') 0s   ABB>B)r   r  r  r  r  r  r
  r   )r  r   r   r   rP   r   r   r   r   )r   zlist[ClusterNode])r$  r   r   r
   )r)  rv   r   r   )r$   r   r5  r   r{   r}   )rP   r   r   r   r   )r   r   r   r   r   r   r   rj   r   r   r  r&  r*  r   r   r6  rR   rA  rV   rG  r[  rU  rZ   r   r  r  s   @r%   r  r  a  sH    *.*.	### '# $(	# #J 
 
=&=8F=	=3!33<3	3,-"/&/8F/	/b	!	3<			!59D 26H(#.H( H( 
 	H(T$)-$	$L#$"	9v* *rX   r  )/r   
__future__r   r   r   loggingrD  abcr   r   collections.abcr   r   r   typingr	   redis.asyncio.clientr
   r   redis.asyncio.clusterr   r   r   redis.exceptionsr   r   r   redis.keyspace_notificationsr   r   r   r   r   r   r   r   r   redis.utilsr   	getLoggerr   r.  AsyncHandlerTr   r   r   r  r"   rX   r%   <module>rs     s   #J #     # > >  . T T 

 
 
 !			8	$ /*D9T?,BBCH# H`~)L ~Ls!C svd*(J d*rX   