Ë
    úmxi=9  ã                  ó¢   — U d Z ddlmZ ddlZddlmZmZmZmZ ddl	m
Z
 erddlmZ ddlmZ  e
e«      Zded	<    G d
„ de«      Z G d„ d«      Zy)a¨  Component file watching utilities.

This module provides the `ComponentFileWatcher`, a utility that watches
component asset directories for changes and notifies a caller-provided callback
with the affected component names. It abstracts the underlying path-watcher
implementation and ensures exception-safe startup and cleanup.

Why this exists
---------------
Streamlit supports advanced Custom Components that ship a package of static
assets (for example, a Vite/Webpack build output). While a user develops their
app, those frontend files may change. The component registry for Custom
Components v2 must stay synchronized with the on-disk assets so that the server
can resolve the up-to-date files.

This watcher exists to keep the registry in sync by listening for changes in
component asset roots and notifying a higher-level manager that can re-resolve
the affected component definitions.

Notes
-----
- Watching is directory-based with a recursive glob ("**/*").
- Common noisy directories (e.g., ``node_modules``) are ignored in callbacks.
- Startup is exception-safe and does not leak partially created watchers.

See Also
--------
- :class:`streamlit.watcher.local_sources_watcher.LocalSourcesWatcher` - watches
  app source files per session to trigger reruns.
- :class:`streamlit.components.v2.component_registry.BidiComponentRegistry` -
  the server-side store of Custom Component v2 definitions that reacts to
  watcher notifications.
é    )ÚannotationsN)ÚTYPE_CHECKINGÚFinalÚProtocolÚcast)Ú
get_logger)ÚCallable©ÚPathr   Ú_LOGGERc                  ó   — e Zd Zdd„Zy)Ú	_HasClosec                 ó   — y ©N© ©Úselfs    úe/home/htdocs/ttos/venv/lib/python3.12/site-packages/streamlit/components/v2/component_file_watcher.pyÚclosez_HasClose.closeB   s   � ó    N©ÚreturnÚNone)Ú__name__Ú
__module__Ú__qualname__r   r   r   r   r   r   A   s   „ Ü r   r   c                  ó¦   — e Zd ZdZdd„Zedd„«       Zdd„Zdd„Zdd„Z	dd„Z
	 	 	 	 dd„Z	 	 	 	 	 	 dd	„Z	 	 	 	 	 	 	 	 dd
„Zdd„Zdd„Zdd„Zdd„Zy)ÚComponentFileWatchera#  Handle file watching for component asset directories.

    Parameters
    ----------
    component_update_callback : Callable[[list[str]], None]
        Callback invoked when files change under any watched directory. It
        receives a list of component names affected by the change.
    c                óŠ   — || _         t        j                  «       | _        i | _        g | _        d| _        i | _        d| _        y)a  Initialize the file watcher.

        Parameters
        ----------
        component_update_callback : Callable[[list[str]], None]
            Callback function to call when components under watched roots change.
            Signature: (affected_component_names)
        F)Ú__pycache__z.cachez.gitz.hgz.mypy_cachez.pytest_cachez.ruff_cachez.svnz.swcz.yarnÚcoverageÚnode_modulesÚvenvN)	Ú_component_update_callbackÚ	threadingÚLockÚ_lockÚ_watched_directoriesÚ_path_watchersÚ_watching_activeÚ_asset_watch_rootsÚ_ignored_dirs)r   Úcomponent_update_callbacks     r   Ú__init__zComponentFileWatcher.__init__O   sN   € ð +DˆÔ'Ü—^‘^Ó%ˆŒ
ð
 ð 	Ô!ð 02ˆÔØ %ˆÔð 46ˆÔð/
ˆÕr   c                ó   — | j                   S )zŸCheck if file watching is currently active.

        Returns
        -------
        bool
            True if file watching is active, False otherwise
        )r*   r   s    r   Úis_watching_activez'ComponentFileWatcher.is_watching_activev   s   € ð ×$Ñ$Ð$r   c                óF   — | j                  «        | j                  |«       y)a{  Start file watching for asset roots.

        Parameters
        ----------
        asset_watch_roots : dict[str, Path]
            Mapping of component names to asset root directories to watch.

        Notes
        -----
        The method is idempotent: it stops any active watchers first, then
        re-initializes watchers for the provided ``asset_watch_roots``.
        N)Ústop_file_watchingÚ_start_file_watching)r   Úasset_watch_rootss     r   Ústart_file_watchingz(ComponentFileWatcher.start_file_watching�   s   € ð 	×ÑÔ!Ø×!Ñ!Ð"3Õ4r   c                óÒ  — | j                   5  | j                  s
	 ddd«       y| j                  D ]  }	 |j                  «        Œ | j                  j                  «        | j                  j                  «        | j                  j                  «        d| _        t
        j                  d«       ddd«       y# t        $ r t
        j                  d«       Y Œ©w xY w# 1 sw Y   yxY w)z´Stop file watching and clean up watchers.

        Notes
        -----
        This method is safe to call multiple times and will no-op if
        watching is not active.
        NzFailed to close path watcherFz,Stopped file watching for component registry)r'   r*   r)   r   Ú	Exceptionr   Ú	exceptionÚclearr(   r+   Údebug)r   Úwatchers     r   r2   z'ComponentFileWatcher.stop_file_watching“   sÙ   € ð �Z‰Zñ 	JØ×(Ò(Ø÷	Jð 	Jð
  ×.Ñ.ò F�ðFØ—M‘M•OðFð ×Ñ×%Ñ%Ô'Ø×%Ñ%×+Ñ+Ô-à×#Ñ#×)Ñ)Ô+Ø$)ˆDÔ!Ü�M‰MÐHÔI÷!	Jð 	Jøô !ò FÜ×%Ñ%Ð&DÖEðFú÷	Jð 	Jús4   �C¤C´B9ÁA,CÂ9CÃCÃCÃCÃC&c                óØ  — | j                   5  | j                  r
	 ddd«       y|st        j                  d«       	 ddd«       y	 | j	                  «       }|€
	 ddd«       y| j                  |«      }| j                  ||«      \  }}|r| j                  |||«       nt        j                  d«       ddd«       y# t        $ r t        j                  d«       Y Œ)w xY w# 1 sw Y   yxY w)a  Internal method to start file watching with the given roots.

        This method is exception-safe: in case of failures while creating
        watchers, any previously created watcher instances are closed and no
        internal state is committed.
        NzNo asset roots to watchz-No directories were watched; staying inactivezFailed to start file watching)
r'   r*   r   r:   Ú_get_default_path_watcher_classÚ_prepare_directories_to_watchÚ_build_watchers_for_directoriesÚ_commit_watch_stater7   r8   )r   r4   Úpath_watcher_classÚdirectories_to_watchÚnew_watchersÚnew_watched_dirss         r   r3   z)ComponentFileWatcher._start_file_watching­   s  € ð �Z‰Zñ 	CØ×$Ò$Ø÷	Cð 	Cñ %Ü—‘Ð7Ô8Ø÷	Cð 	CðCØ%)×%IÑ%IÓ%KÐ"Ø%Ð-à÷	Cð 	Cð (,×'IÑ'IØ%ó(Ð$ð 26×1UÑ1UØ&Ð(<ó2Ñ.�Ð.ñ
  Ø×,Ñ,Ø$Ð&6Ð8Iõô —M‘MÐ"QÔR÷9	Cð 	Cøô: ò CÜ×!Ñ!Ð"AÖBðCú÷;	Cð 	Cús5   �C ¤C ÁB<Á"AB<Â<CÃC ÃCÃC Ã C)c                óX   — ddl m}m}  |«       }||u rt        j	                  d«       y|S )a  Return the default path watcher class.

        Returns
        -------
        type | None
            The concrete path watcher class to instantiate, or ``None`` if
            the NoOp watcher is configured and file watching should be
            skipped.
        r   )ÚNoOpPathWatcherÚget_default_path_watcher_classz8NoOpPathWatcher in use; skipping component file watchingN)Ústreamlit.watcher.path_watcherrF   rG   r   r:   )r   rF   rG   rA   s       r   r=   z4ComponentFileWatcher._get_default_path_watcher_classÔ   s0   € ÷	
ñ
 <Ó=ÐØ Ñ0Ü�M‰MÐTÔUØØ!Ð!r   c                ó¶   — i }|j                  «       D ]C  \  }}t        |j                  «       «      }||vrg ||<   |||   vsŒ0||   j                  |«       ŒE |S )a”  Build a mapping of directory to component names.

        Parameters
        ----------
        asset_watch_roots : dict[str, Path]
            Mapping of component names to their asset root directories.

        Returns
        -------
        dict[str, list[str]]
            A map from absolute directory path to a deduplicated list of
            component names contained in that directory.
        )ÚitemsÚstrÚresolveÚappend)r   r4   rB   Ú	comp_nameÚrootÚ	directorys         r   r>   z2ComponentFileWatcher._prepare_directories_to_watché   su   € ð  68ÐØ0×6Ñ6Ó8ò 	B‰OˆI�tÜ˜DŸL™L›NÓ+ˆIØÐ 4Ñ4Ø24Ð$ YÑ/ØÐ 4°YÑ ?Ò?Ø$ YÑ/×6Ñ6°yÕAð	Bð $Ð#r   c                ó<  — g }i }|j                  «       D ]c  \  }}	 | j                  t        |«      «      } |||dd¬«      }|j                  t	        d|«      «       |||<   t
        j                  d||«       Œe ||fS # t        $ r | j                  |«       ‚ w xY w)a¤  Create watchers for directories with rollback on failure.

        Parameters
        ----------
        path_watcher_class : type
            The path watcher class to instantiate for each directory.
        directories_to_watch : dict[str, list[str]]
            A map of directory to the associated component name list.

        Returns
        -------
        tuple[list[_HasClose], dict[str, list[str]]]
            The list of created watcher instances and the watched directory
            mapping.

        Raises
        ------
        Exception
            Propagates any exception during watcher creation after closing
            already-created watchers.
        z**/*F)Úglob_patternÚallow_nonexistentr   z2Prepared watcher for directory %s (components: %s))	rJ   Ú_make_directory_callbackÚtuplerM   r   r   r:   r7   Ú_rollback_watchers)	r   rA   rB   rC   rD   rP   Úcomponent_namesÚcbr;   s	            r   r?   z4ComponentFileWatcher._build_watchers_for_directories  sÅ   € ð0 )+ˆØ13Ðà*>×*DÑ*DÓ*Fò 	Ñ&ˆI�ðØ×2Ñ2´5¸Ó3IÓJ�ñ -ØØØ!'Ø&+ô	�ð ×#Ñ#¤D¨°gÓ$>Ô?Ø.=Ð  Ñ+Ü—‘ØHØØ#õð	ð. Ð-Ð-Ð-øô ò à×'Ñ'¨Ô5Øðús   œAA?Á?Bc                ó    — || _         || _        t        |«      | _        d| _        t
        j                  dt        | j                  «      «       y)a›  Commit created watchers and mark watching active.

        Parameters
        ----------
        new_watchers : list[_HasClose]
            Fully initialized watcher instances.
        new_watched_dirs : dict[str, list[str]]
            Mapping from directory to component names.
        asset_watch_roots : dict[str, Path]
            The asset roots used to initialize watchers; stored for reference.
        Tz(Started file watching for %d directoriesN)r)   r(   Údictr+   r*   r   r:   Úlen)r   rC   rD   r4   s       r   r@   z(ComponentFileWatcher._commit_watch_state6  sF   € ð" +ˆÔØ$4ˆÔ!Ü"&Ð'8Ó"9ˆÔØ $ˆÔÜ�‰Ø6¼¸D×<UÑ<UÓ8Võ	
r   c                ó|   — |D ]  }	 |j                  «        Œ y# t        $ r t        j                  d«       Y Œ7w xY w)zÎClose any created watchers when setup fails.

        Parameters
        ----------
        watchers : list[_HasClose]
            Watcher instances that were successfully created before a failure.
        z,Failed to close path watcher during rollbackN)r   r7   r   r8   )r   ÚwatchersÚws      r   rV   z'ComponentFileWatcher._rollback_watchersO  sF   € ð ò 	RˆAðRØ—‘•	ñ	Røô ò RÜ×!Ñ!Ð"PÖQðRús   ‡š;º;c                ó   ‡ ‡— dˆˆ fd„}|S )zHCreate a callback for a directory watcher that captures component names.c                ó¸   •— ‰j                  | «      rt        j                  d| «       y t        j                  d| ‰«       ‰j                  t	        ‰«      «       y )Nz&Ignoring change in noisy directory: %sz6Directory change detected: %s, checking components: %s)Ú_is_in_ignored_directoryr   r:   Ú_handle_component_changeÚlist)Úchanged_pathÚcompsr   s    €€r   Úcallbackz?ComponentFileWatcher._make_directory_callback.<locals>.callback`  sL   ø€ Ø×,Ñ,¨\Ô:Ü—‘ÐFÈÔUØÜ�M‰MØHØØôð
 ×)Ñ)¬$¨u«+Õ6r   )rd   rK   r   r   r   )r   re   rf   s   `` r   rT   z-ComponentFileWatcher._make_directory_callback]  s   ù€ ö		7ð ˆr   c                óŠ   — | j                   sy	 | j                  |«       y# t        $ r t        j	                  d«       Y yw xY w)zÏHandle component changes for both directory and file events.

        Parameters
        ----------
        affected_components : list[str]
            List of component names affected by the change
        Nz Component update callback raised)r*   r$   r7   r   r8   )r   Úaffected_componentss     r   rb   z-ComponentFileWatcher._handle_component_changem  sE   € ð ×$Ò$Øð	BØ×+Ñ+Ð,?Õ@øÜò 	Bä×ÑÐ@ÖAð	Bús   �! ¡AÁAc                ó¾   ‡— 	 ddl m} t         ||«      j                  «       j                  «      Št        ˆfd„| j                  D «       «      S # t        $ r Y yw xY w)ae  Return True if the changed path is inside an ignored directory.

        Parameters
        ----------
        changed_path : str
            The filesystem path that triggered the change event.

        Returns
        -------
        bool
            True if the path is located inside one of the ignored directories,
            False otherwise.
        r   r
   c              3  ó&   •K  — | ]  }|‰v –— Œ
 y ­wr   r   )Ú.0ÚignoredÚpartss     €r   ú	<genexpr>z@ComponentFileWatcher._is_in_ignored_directory.<locals>.<genexpr>‘  s   øè ø€ ÒJ¨G�w %Ô'ÑJùs   ƒF)Úpathlibr   ÚsetrL   rm   Úanyr,   r7   )r   rd   Ú_Pathrm   s      @r   ra   z-ComponentFileWatcher._is_in_ignored_directory  sR   ø€ ð	Ý-ä™˜lÓ+×3Ñ3Ó5×;Ñ;Ó<ˆEÜÓJ°t×7IÑ7IÔJÓJÐJøÜò 	Ùð	ús   ƒAA Á	AÁAN)r-   zCallable[[list[str]], None]r   r   )r   Úbool)r4   údict[str, Path]r   r   r   )r   ztype | None)r4   rt   r   údict[str, list[str]])rA   ÚtyperB   ru   r   z,tuple[list[_HasClose], dict[str, list[str]]])rC   úlist[_HasClose]rD   ru   r4   rt   r   r   )r]   rw   r   r   )re   ztuple[str, ...]r   zCallable[[str], None])rh   z	list[str]r   r   )rd   rK   r   rs   )r   r   r   Ú__doc__r.   Úpropertyr0   r5   r2   r3   r=   r>   r?   r@   rV   rT   rb   ra   r   r   r   r   r   E   s¬   „ ñó%
ðN ò%ó ð%ó5ó$Jó4%CóN"ð*$Ø!0ð$à	ó$ð22.Ø"&ð2.Ø>Rð2.à	5ó2.ðh
à%ð
ð /ð
ð +ð	
ð
 
ó
ó2Róó Bô$r   r   )rx   Ú
__future__r   r%   Útypingr   r   r   r   Ústreamlit.loggerr   Úcollections.abcr	   ro   r   r   r   Ú__annotations__r   r   r   r   r   ú<module>r      sN   ðò  õD #ã ß 7Ó 7å 'áÝ(Ýñ ˜HÓ%€ˆÓ %ô!�ô !÷Nò Nr   