Ë
    rPêj)K  ã                   óD  — 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m	Z	 ddl
m
Z
 ddlmZmZmZ ddlZddlmZ dd	lmZ dd
lmZmZmZmZ  ej2                  e«      ZdZ	 dZdZdZdZ dZ!dZ"i Z#e$e%e&e'e&z  dz  e&f   e%dejP                  f   f   e)d<    ejT                  «       Z+ G d„ de,«      Z-e G d„ d«      «       Z.e G d„ d«      «       Z/e G d„ d«      «       Z0e G d„ d«      «       Z1e G d„ d«      «       Z2ee/e0e1e2f   Z3ded e
dz  fd!„Z4d"e&d#e&d e3dz  fd$„Z5e%ed%   e&f   Z6d&e6dz  d e$e&e&f   fd'„Z7d(ee8   d)eg e'f   d ee8   fd*„Z9d+e&d,e&d-e'e&z  dz  d&e6dz  d)eg e'f   d ee3   fd.„Z: G d/„ d0«      Z;d1e,d e<dz  fd2„Z=d1e,d e>dz  fd3„Z? G d4„ dej€                  «      ZAy)5a*  Live-following of file changes in a bucket.

The Hub exposes a server-sent events feed of per-file changes for buckets:

```
GET /api/buckets/{bucket_id}/events?cursor=<opaque> | ?since=<ISO8601>
Accept: text/event-stream
```

A consumer can keep its view of a bucket fresh by applying the batches as they happen instead of re-listing
directories. This module implements the client side of that feed:

- [`parse_follow_event`] turns a raw SSE event into a typed event. Unknown or malformed events are dropped so
  the Hub can add event types without breaking older clients.
- [`BucketFollower`] is a daemon thread owning one (re)connecting stream for one bucket. It maintains the
  resume point, reconnects with exponential backoff, and hands the batches over to callbacks provided by the
  caller (see [`HfFileSystem`]).

The feed is best-effort by design: when it cannot be followed (deployment where live-follow is disabled, token
without access to the bucket), the follower stops and the consumer is left with its default behavior.

Event vocabulary:

| event       | payload                                | meaning                                                               |
| ----------- | -------------------------------------- | --------------------------------------------------------------------- |
| `ready`     | `{cursor?}`                            | replay is done, the stream now follows live changes                    |
| `changes`   | `{cursor, changes: [{path, op, ...}]}` | batch of per-file changes; `cursor` resumes strictly after the batch   |
| `reset`     | `{reason}`                             | resume point older than the replay buffer: reconcile and re-subscribe  |
| `reconnect` | `{cursor?}`                            | server-directed end of stream (pod rotation, shutdown,...)             |
é    N)ÚCallableÚIterator)Ú	dataclassÚfield)Údatetime)ÚAnyÚLiteralÚUnioné   )Úlogging)Ú	SSEClient)Úbuild_hf_headersÚget_sessionÚhf_raise_for_statusÚparse_datetimeg     €V@ç      $@g      T@g      ð?é   )i‘  i“  i”  iõ  ÚBucketFollowerÚBUCKET_FOLLOWERSc                   ó   — e Zd ZdZy)ÚBucketEventsUnavailablezFThe live-follow feed cannot be followed for this bucket: do not retry.N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__© ó    úh/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/huggingface_hub/_bucket_live_follow.pyr   r   R   s   „ ÚPr   r   c                   ó|   — e Zd ZU dZeed<   ed   ed<   dZedz  ed<   dZ	edz  ed<   dZ
edz  ed<   dZedz  ed	<   y)
ÚBucketFileChangeaQ  
    A single file change reported by the live-follow feed of a bucket.

    Only `path` and `op` are always set: an `update` carries only the fields that changed, so a field left to
    `None` means "unchanged" (or, for `xet_hash`, possibly "not readable with this token") and never "cleared".

    Args:
        path (`str`):
            Path of the file in the bucket.
        op (`Literal["add", "update", "delete"]`):
            What happened to the file.
        size (`int`, *optional*):
            Size of the file in bytes, when reported.
        xet_hash (`str`, *optional*):
            Xet hash of the file, when reported.
        uploaded_at (`datetime`, *optional*):
            Upload instant, set on every add and re-upload.
        mtime (`datetime`, *optional*):
            Client-provided modification time, when reported.
    Úpath©ÚaddÚupdateÚdeleteÚopNÚsizeÚxet_hashÚuploaded_atÚmtime)r   r   r   r   ÚstrÚ__annotations__r	   r'   Úintr(   r)   r   r*   r   r   r   r    r    V   sR   … ñð* ƒIØÐ)Ñ*Ó*Ø€Dˆ#�‰*ÓØ€Hˆc�D‰jÓØ#'€K�˜D‘Ó'Ø!€Eˆ8�d‰?Ô!r   r    c                   ó   — e Zd ZU edz  ed<   y)Ú_ReadyNÚcursor©r   r   r   r+   r,   r   r   r   r/   r/   u   ó   … à�$‰JÔr   r/   c                   ó:   — e Zd ZU eed<    ee¬«      Zee   ed<   y)Ú_Changesr0   )Údefault_factoryÚchangesN)	r   r   r   r+   r,   r   Úlistr6   r    r   r   r   r4   r4   z   s   … àƒKÙ&+¸DÔ&A€GˆTÐ"Ñ#ÔAr   r4   c                   ó   — e Zd Zy)Ú_ResetN)r   r   r   r   r   r   r9   r9   €   s   „ àr   r9   c                   ó   — e Zd ZU edz  ed<   y)Ú
_ReconnectNr0   r1   r   r   r   r;   r;   …   r2   r   r;   ÚvalueÚreturnc                 ó@   — t        | t        «      r| rt        | «      S d S ©N)Ú
isinstancer+   r   )r<   s    r   Ú_parse_datetimerA   �   s   € Ü$.¨u´cÔ$:¹uŒ>˜%Ó ÐNÈ$ÐNr   ÚnameÚdatac                 ó€  — 	 |j                  «       rt        j                  |«      ni }t        |t        «      st        j                  d| |dd «       y| xdk(  r5 |j                  d«      }t        t        |t        «      r|¬«      S d¬«      S xdk(  �r |j                  d«      |j                  d«      }}t        |t        «      rt        |t        «      st        j                  d	|dd «       yg }|D ]§  }t        |t        «      r|j                  d
«      dvrŒ'|j                  t        |j                  dd«      |d
   |j                  d«      |j                  d«      t        |j                  d«      «      t        |j                  d«      «      ¬«      «       Œ© t        ||¬«      S xdk(  r t!        «       S dk(  r4|j                  d«      }t#        t        |t        «      r|¬«      S d¬«      S 	 t        j                  d| «       y# t        j                  $ r t        j                  d| |dd «       Y yw xY w)a’  
    Parse a raw live-follow event into a typed event.

    Args:
        name (`str`):
            Value of the `event:` field: `ready`, `changes`, `reset` or `reconnect`.
        data (`str`):
            JSON payload of the `data:` field (may be empty).

    Returns:
        `Any` | `None`: The parsed event, or `None` when it must be ignored (unknown event name or malformed
        payload).
    z.live-follow: ignoring malformed %r payload: %sNéÈ   z:live-follow: ignoring %r payload that is not an object: %sÚreadyr0   ©r0   r6   z@live-follow: ignoring 'changes' event without cursor/changes: %sr&   r"   r!   Ú r'   ÚxetHashÚ
uploadedAtr*   )r!   r&   r'   r(   r)   r*   ©r0   r6   ÚresetÚ	reconnectz+live-follow: ignoring unknown event type %r)ÚstripÚjsonÚloadsÚJSONDecodeErrorÚloggerÚdebugr@   ÚdictÚgetr/   r+   r7   Úappendr    rA   r4   r9   r;   )rB   rC   Úpayloadr0   Úraw_changesr6   Úitems          r   Úparse_follow_eventrZ   ‘   sê  € ðØ&*§j¡j¤l”$—*‘*˜TÔ"¸ˆô �gœtÔ$Ü�‰ÐQÐSWÐY]Ð^bÐ_bÐYcÔdØà
ÝØ—[‘[ Ó*ˆFÜ¬:°f¼cÔ+B ÔMÐMÈÔMÐMæØ")§+¡+¨hÓ"7¸¿¹ÀYÓ9O�KˆFÜ˜f¤cÔ*´*¸[Ì$Ô2OÜ—‘Ð_ÐaeÐfjÐgjÐakÔlØØˆGØ#ò �Ü! $¬Ô-°·±¸$³ÐGbÑ1bØØ—‘Ü$Ø!ŸX™X f¨bÓ1Ø ™:Ø!ŸX™X fÓ-Ø!%§¡¨)Ó!4Ü$3°D·H±H¸\Ó4JÓ$KÜ-¨d¯h©h°wÓ.?Ó@ôõ	ðô  6°7Ô;Ð;åÜ“8ˆOãØ—[‘[ Ó*ˆFÜ¬z¸&Ä#Ô/F VÔQÐQÈDÔQÐQàÜ�L‰LÐFÈÔMØøôU ×Ñò Ü�‰ÐEÀtÈTÐRVÐSVÈZÔXÙðús   ‚'H È-H=È<H=)r0   ÚsinceÚliveÚresumec                 ó^   — | x  r dk(  r\  dk(  r} d|iS    r dk(  r\  dk(  r}|rd|iS  	 i S )Né   r0   r[   r   )r]   r0   r[   s      r   Ú_resume_paramsr`   Ô   sE   € Ø
ßÑ‹hÑØ˜fÐ%Ð%ð  çÐ‹gÐ¡Ø˜UÐ#Ð#ð àØˆIr   ÚchunksÚshould_stopc              #   ó4   K  — | D ]  } |«       r y|–— Œ y­w)zø
    Pass response chunks through until `should_stop` returns true.

    The check runs when a chunk arrives, so a stream carrying nothing to parse is abandoned at its next
    keep-alive comment instead of being read until the server ends it.
    Nr   )ra   rb   Úchunks      r   Ú_iter_untilre   Þ   s$   è ø€ ð ò ˆÙŒ=ÙØ‹ñùs   ‚ÚendpointÚ	bucket_idÚtokenc              #   óL  K  — t        j                  t        t        t        d¬«      }t	        «       j                  d| › d|› d�i t        |¬«      ¥ddi¥t        |«      |¬	«      5 }|j                  t        v r%t        d
|j                  › d|j                  › �«      ‚|j                  j                  d«      xs dj                  d«      s#t        |«       t        d|j                  › �«      ‚t        |«       	 t!        t#        |j%                  «       |«      «      j'                  «       D ])  }t)        |j*                  |j,                  «      }|€Œ&|–— Œ+ 	 ddd«       y# t         j.                  $ r!}	t0        j3                  d||	«       Y d}	~	Œ8d}	~	ww xY w# 1 sw Y   yxY w­w)aF  
    Open a single live-follow session and yield the parsed events until the stream ends.

    Ends silently on EOF, on read timeout and when `should_stop` returns true. Raises
    [`BucketEventsUnavailable`] if the feed cannot be followed and [`~utils.HfHubHTTPError`] for anything else
    the caller must decide about.
    r   )ÚconnectÚreadÚwriteÚpoolÚGETú/api/buckets/z/events©rh   Úacceptztext/event-stream)ÚheadersÚparamsÚtimeoutzstatus z on zcontent-typerH   zunexpected content-type on Nz+live-follow: stream error on bucket %s (%s))Úhttpx2ÚTimeoutÚCONNECT_TIMEOUTÚSTREAM_READ_TIMEOUTr   Ústreamr   r`   Ústatus_codeÚ_UNFOLLOWABLE_STATUS_CODESr   Úurlrr   rU   Ú
startswithr   r   re   Ú
iter_bytesÚeventsrZ   ÚeventrC   Ú	HTTPErrorrR   rS   )
rf   rg   rh   r]   rb   rt   Úresponser€   ÚparsedÚes
             r   Ú_follow_eventsr…   ë   sŠ  è ø€ ô �n‰n¤_Ô;NÔVeÐlpÔq€GÜ	‹×	Ñ	ØØˆ*�M ) ¨GÐ4ØPÔ#¨%Ô0ÐP°(Ð<OÑPÜ˜fÓ%Øð 
ó 
ð Vð 
Ø×ÑÔ#=Ñ=Ü)¨G°H×4HÑ4HÐ3IÈÈhÏlÉlÈ^Ð*\Ó]Ð]Ø× Ñ ×$Ñ$ ^Ó4Ò:¸×FÑFÐGZÔ[ä Ô)Ü)Ð,GÈÏÉÀ~Ð*VÓWÐWÜ˜HÔ%ð
	VÜ"¤;¨x×/BÑ/BÓ/DÀkÓ#RÓS×ZÑZÓ\ò !�Ü+¨E¯K©K¸¿¹ÓD�ØÑ%Ø “Lñ!÷'Vð Vøô. ×Ñò 	Vä�L‰LÐFÈ	ÐST×UÑUûð	Vú÷/Vð VüsP   ‚A#F$Á%BFÃ:AE!ÅE!ÅFÅ	F$Å!FÅ4FÆFÆFÆFÆF!ÆF$c                   ó8   — e Zd ZdZdededdfd„Zdefd„Zd	d„Zy)
Ú_Backoffz@Exponential backoff, capped, reset by the first healthy session.ÚbaseÚ	max_delayr=   Nc                 ó.   — || _         || _        d| _        y ©Nr   )Ú_baser‰   Ú_attempt)Úselfrˆ   r‰   s      r   Ú__init__z_Backoff.__init__  s   € ØˆŒ
Ø"ˆŒØˆ�r   c                 ó�   — t        | j                  d| j                  z  z  | j                  «      }| xj                  dz  c_        |S )Nr_   r   )ÚminrŒ   r�   r‰   ©rŽ   Údelays     r   Únextz_Backoff.next  s7   € Ü�D—J‘J  D§M¡MÑ!1Ñ1°4·>±>ÓBˆØ�Š˜Ñ�Øˆr   c                 ó   — d| _         y r‹   )r�   ©rŽ   s    r   rL   z_Backoff.reset#  s	   € Øˆ�r   ©r=   N)r   r   r   r   Úfloatr�   r”   rL   r   r   r   r‡   r‡     s0   „ ÙJð˜Uð ¨uð ¸ó ð
�eó ô
r   r‡   r„   c                 ó˜   — t        | dd«      }|�|j                  j                  d«      nd}	 |�t        |«      S dS # t        $ r Y yw xY w)zAThe server `Retry-After` hint (rate limiting,...) if it sent one.r‚   Nzretry-after)Úgetattrrr   rU   r˜   Ú
ValueError)r„   r‚   Úhints      r   Ú_retry_afterr�   '  sX   € ä�q˜* dÓ+€HØ2:Ð2Fˆ8×Ñ×Ñ Ô.ÈD€DðØ"Ð.Œu�T‹{Ð8°DÐ8øÜò Ùðús   ®= »= ½	A	ÁA	c                 ó<   — t        | dd «      }|�|j                  S d S )Nr‚   )rš   rz   )r„   r‚   s     r   Ú_status_coderŸ   1  s&   € Ü�q˜* dÓ+€HØ#+Ð#7ˆ8×ÑÐA¸TÐAr   c                   ó¼   ‡ — e Zd ZdZddœdededeez  dz  deee   gdf   deg df   d	eg ef   d
dfˆ fd„Z	dd„Z
d
efd„Zdd„Zd
efd„Zd
efd„Zded
dfd„Zˆ xZS )r   aþ  
    Daemon thread keeping one bucket's event stream alive and reporting the changes it carries.

    One instance owns exactly one stream, for one bucket. Batches are handed over to `on_changes`. Whenever no
    usable resume point is known (first session, or the server cannot replay that far back), `on_reconcile` is
    called first: the consumer is expected to drop what it holds for that bucket, so that the replay that
    follows starts from a fresh state. The thread ends on its own when `is_alive` turns false, when the feed is
    not served, or after too many sessions ending before they were even ready. It keeps no strong reference to
    its consumer, which can therefore be garbage collected while the thread is running.

    Args:
        endpoint (`str`):
            Endpoint of the Hub.
        bucket_id (`str`):
            ID of the bucket to follow (e.g. `"username/my-bucket"`).
        token (`bool` or `str`, *optional*):
            Token used to consume the feed, resolved again at every (re)connect.
        on_changes (`Callable[[list[BucketFileChange]], None]`):
            Called with each batch of changes received from the feed.
        on_reconcile (`Callable[[], None]`):
            Called before (re)subscribing without a usable cursor.
        is_alive (`Callable[[], bool]`):
            Whether the consumer still cares about the updates. Checked before every session and whenever the
            stream sends anything, so a stream carrying no change is abandoned within one keep-alive.
    Nrp   rf   rg   rh   Ú
on_changesÚon_reconcileÚis_aliver=   c                óÐ   •— t         ‰| �  dd|› �¬«       || _        d| _        || _        || _        || _        || _        || _        t        j                  «       | _        d | _        y )NTzhf-bucket-live-follow-)ÚdaemonrB   F)Úsuperr�   rg   Ú
subscribedÚ	_endpointÚ_tokenÚ_on_changesÚ_on_reconcileÚ	_is_aliveÚ	threadingÚEventÚ_stop_eventÚ_resume)rŽ   rf   rg   rh   r¡   r¢   r£   Ú	__class__s          €r   r�   zBucketFollower.__init__Q  sh   ø€ ô 	‰Ñ Ð-CÀIÀ;Ð+OÐÔPØ"ˆŒàˆŒØ!ˆŒØˆŒØ%ˆÔØ)ˆÔØ!ˆŒÜ$Ÿ?™?Ó,ˆÔØ+/ˆ�r   c                 ó8   — | j                   j                  «        y)zgAsk the follower to stop. Returns before the thread ends: it stops once the stream yields or times out.N)r¯   Úsetr–   s    r   ÚstopzBucketFollower.stopg  s   € à×Ñ×ÑÕr   c                 ó\   — | j                   j                  «       xs | j                  «        S )zWWhether to give up: explicitly asked to, or the consumer holding this follower is gone.)r¯   Úis_setr¬   r–   s    r   Ú_should_stopzBucketFollower._should_stopk  s&   € à×Ñ×&Ñ&Ó(Ò@°·±Ó0@Ð,@Ð@r   c                 ó  — t        t        t        «      }d}| j                  «       sÏ| j                  €'d| j                  «       f| _        | j                  «        	 | j                  «       }|r(|j)                  «        d}| j!                  t*        «       nO|dz  }|t,        k\  r"t        j                  d|| j                  «       y | j!                  |j'                  «       «       | j                  «       sŒÎy y # t        $ r0}t        j                  d| j                  › d|› d�«       Y d }~y d }~wt        $ rÍ}t        |«      dk(  rr| j                  x  r% dk(  r \  dk(  r  d| j                  «       f| _        n?   r dk(  r\  dk(  r	 d	| _        n( 	 t        j                  d
| j                  |«       Y d }~y t        |«      }| j!                  |�t#        ||j$                  «      n|j'                  «       «       Y d }~�Œèd }~ww xY w)Nr   r[   zCannot live-follow bucket 'z' (z#): its listings won't be refreshed.i�  r_   r0   )r\   rH   z(live-follow: giving up on bucket %s (%s)r   zMlive-follow: %s sessions for bucket %s ended before 'ready'; not following it)r‡   ÚRETRY_BASE_DELAYÚRETRY_MAX_DELAYr·   r°   Ú_fetch_updated_atr«   Ú_follow_oncer   rR   Úwarningrg   Ú	ExceptionrŸ   rS   r�   Ú_sleepr‘   r‰   r”   rL   ÚRECONNECT_MIN_DELAYÚMAX_SESSIONS_WITHOUT_READY)rŽ   ÚbackoffÚsessions_without_readyÚhealthyr„   r“   s         r   ÚrunzBucketFollower.runo  sº  € ÜÔ+¬_Ó=ˆØ!"ÐØ×#Ñ#Ô%Ø�|‰|Ð#ð !(¨×)?Ñ)?Ó)AÐB�”Ø×"Ñ"Ô$ðØ×+Ñ+Ó-�ñ( Ø—‘”Ø)*Ð&Ø—‘Ô/Õ0à&¨!Ñ+Ð&Ø)Ô-GÒGÜ—L‘LØgØ.ØŸ™ôð
 Ø—‘˜GŸL™L›NÔ+ðQ ×#Ñ#×%øô +ò Ü—‘Ð!<¸T¿^¹^Ð<LÈCÐPQÈsÐRuÐvÔwÜûÜò Ü “? cÒ)ð Ÿ,™,ß*™]›h¨˜]Ø,3°T×5KÑ5KÓ5MÐ+N˜D�Lð +ç)˜\›g qØ+7˜D�Lð *àÜ"ŸL™LÐ)SÐUY×UcÑUcÐefÔgÜ"Ü$ Q›�Ø—‘¸UÐ=NœC  w×'8Ñ'8Ô9ÐT[×T`ÑT`ÓTbÔcÝûðús,   ÁC7 Ã7	HÄ &D+Ä+HÄ7A;G?Æ7AG?Ç?Hc                 óÄ  — d}t        | j                  | j                  | j                  | j                  | j
                  «      D ]™  }|xt        d˜ x�\  } |�	d|f| _        d}d| _        n_ xt        d˜ x�"\  }} |r| j                  |«       d|f| _        n2 xt        d˜ x�\    d| _         y t        d˜ x�\  }|�	d|f| _         y | j                  «       sŒ—|c S  |S )zÃ
        Consume a single stream. Returns whether the session was healthy, i.e. whether it reached the `ready`
        state (a server-directed `reconnect` or `reset` counts as healthy).
        FrG   Nr0   TrK   r   )r…   r¨   rg   r©   r°   r·   r/   r§   r4   rª   r9   r;   )rŽ   rF   r€   r0   r6   s        r   r¼   zBucketFollower._follow_onceœ  sð   € ð
 ˆÜ# D§N¡N°D·N±NÀDÇKÁKÐQU×Q]ÑQ]Ð_c×_pÑ_pÓqò 	ˆEØØ*”V×*àÐ)Ø(0°&Ð'9˜œØ �EØ&*�D•Oð +ð >”X×=Ð=áØ×(Ñ(¨Ô1Ø$,¨fÐ#5�D•Lð	 >ð
 ”V–Xà#'�D”LÙð ô  Ö.ØÐ)Ø(0°&Ð'9˜œÙð /ð × Ñ Õ"Ø’ð-	ð. ˆr   c                 óˆ  — 	 t        «       j                  | j                  › d| j                  › �t	        | j
                  ¬«      t        ¬«      }t        |«       |j                  «       j                  d«      }t        |t        «      r|S dS # t        $ r+}t        j                  d| j                  |«       Y d}~yd}~ww xY w)zNGet the instant (ISO8601) to resume from. Empty string if it cannot be probed.ro   rp   )rr   rt   Ú	updatedAtrH   z6live-follow: cannot probe updatedAt for bucket %s (%s)N)r   rU   r¨   rg   r   r©   rw   r   rO   r@   r+   r¾   rR   rS   )rŽ   r‚   Ú
updated_atr„   s       r   r»   z BucketFollower._fetch_updated_at»  s¥   € ð	Ü"“}×(Ñ(Ø—>‘>Ð" -°·±Ð/?Ð@Ü(¨t¯{©{Ô;Ü'ð )ó ˆHô
   Ô)Ø!Ÿ™›×,Ñ,¨[Ó9ˆJÜ!+¨J¼Ô!<�:ÐDÀ"ÐDøÜò 	Ü�L‰LÐQÐSW×SaÑSaÐcdÔeÜûð	ús   ‚BB ÂB Â	CÂ!B<Â<Cr“   c                 ó:   — | j                   j                  |«       y r?   )r¯   Úwaitr’   s     r   r¿   zBucketFollower._sleepÊ  s   € Ø×Ñ×Ñ˜eÕ$r   r—   )r   r   r   r   r+   Úboolr   r7   r    r�   r´   r·   rÅ   r¼   r»   r˜   r¿   Ú__classcell__)r±   s   @r   r   r   6  sÊ   ø„ ñð> $(ò0ð ð0ð ð	0ð
 �c‰z˜DÑ ð0ð ˜dÐ#3Ñ4Ð5°tÐ;Ñ<ð0ð ˜r 4˜xÑ(ð0ð ˜2˜t˜8Ñ$ð0ð 
õ0ó,ðA˜dó Aó+,ðZ˜dó ð> 3ó ð%˜Eð % d÷ %r   )Br   rO   r­   ÚweakrefÚcollections.abcr   r   Údataclassesr   r   r   Útypingr   r	   r
   ru   rH   r   Ú_hot_reload.sse_clientr   Úutilsr   r   r   r   Ú
get_loggerr   rR   rx   rw   r¹   rº   rÀ   rÁ   r{   r   rT   Útupler+   rÌ   ÚWeakValueDictionaryr,   ÚLockÚBUCKET_FOLLOWERS_LOCKr¾   r   r    r/   r4   r9   r;   Ú_FollowEventrA   rZ   ÚResumePointr`   Úbytesre   r…   r‡   r˜   r�   r-   rŸ   ÚThreadr   r   r   r   ú<module>rÝ      sµ  ðòó> Û Û ß .ß (Ý ß &Ñ &ã å Ý -ß UÓ Uð 
ˆ×	Ñ	˜HÓ	%€ð Ð Ø eà€ØÐ Ø€ØÐ ØÐ ð 2Ð ð tvÐ �$�u˜S $¨¡*¨tÑ"3°SÐ8Ñ9¸5ÐAQÐSZ×SnÑSnÐAnÑ;oÐoÑpÓ uØ&˜	Ÿ™Ó(Ð ôQ˜iô Qð ÷"ð "ó ð"ð< ÷ð ó ðð ÷Bð Bó ðBð
 ÷	ð 	ó ð	ð ÷ð ó ðð �V˜X v¨zÐ9Ñ:€ðO˜3ð O 8¨d¡?ó Oð:˜Sð :¨ð :°¸tÑ0Có :ð@ �GÐ5Ñ6¸Ð;Ñ<€ð˜;¨Ñ-ð °$°s¸C°x±.ó ð
˜ ™ð 
°h¸rÀ4¸xÑ6Hð 
ÈXÐV[É_ó 
ð(VØð(Vàð(Vð �#‰:˜Ñð(Vð ˜$Ñð	(Vð
 ˜"˜d˜(Ñ#ð(Vð ˆlÑó(V÷Vñ ð"�Ið  %¨$¡,ó ðB�Ið B #¨¡*ó Bô
U%�Y×%Ñ%õ U%r   