
    ȯeiQ                       U d Z ddlm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 ddlmZmZ ddlZddlmZ dd	lmZ dd
lmZ ddlmZ  ee      Zded<   ddZe G d d             Ze G 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+	 d)	 	 	 d*dZ,y)+a  Discovery utilities for Component v2 manifests in installed packages.

The scanner searches installed distributions for a ``pyproject.toml`` with
``[tool.streamlit.component]`` configuration and extracts the component
manifests along with their package roots.

The implementation prioritizes efficiency and safety by filtering likely
candidates and avoiding excessive filesystem operations.
    )annotationsN)ThreadPoolExecutoras_completed)	dataclass)Path)AnyFinal)utils)ComponentPathUtils)StreamlitComponentRegistryError)
get_loggerr	   _LOGGERc                &    | j                  dd      S )a  Normalize a distribution name to an importable package name.

    This helper converts hyphens to underscores to derive a best-effort
    importable module/package name from a distribution name.

    Parameters
    ----------
    dist_name : str
        The distribution/project name (e.g., "my-awesome-component").

    Returns
    -------
    str
        The normalized package name suitable for import lookups
        (e.g., "my_awesome_component").
    -_)replace)	dist_names    j/var/www/html/glpi_dashboard/venv/lib/python3.12/site-packages/streamlit/components/v2/manifest_scanner.py_normalize_package_namer   -   s    " S#&&    c                  0    e Zd ZU dZded<   ded<   ded<   y)ComponentManifestzParsed component manifest data.strnameversionzlist[ComponentConfig]
componentsN)__name__
__module____qualname____doc____annotations__ r   r   r   r   A   s    )
IL%%r   r   c                  V    e Zd ZU dZded<   dZded<   ed
d       Zedd       Zdd	Z	y)ComponentConfigzStructured configuration for a single component entry.

    Parameters
    ----------
    name
        Component name as declared in ``pyproject.toml``.
    asset_dir
        Optional relative directory containing component assets.
    r   r   Nz
str | None	asset_dirc                    | j                  d      }t        |t              r|st        d      | j                  d      }|t        |t              st        d      t	        ||      S )a  Create a ComponentConfig from a raw dict.

        Parameters
        ----------
        config
            Raw component dictionary parsed from TOML.

        Returns
        -------
        ComponentConfig
            Parsed and validated component configuration.
        r   z-Component entry missing required 'name' fieldr%   z'asset_dir' must be a string)r   r%   )get
isinstancer   
ValueErrorr$   )config
name_valueasset_dir_values      r   	from_dictzComponentConfig.from_dictY   sf     ZZ'
*c**LMM **[1&z/3/O;<<%
 	
r   c                    	 t         j                  |       S # t        $ r }t        j	                  d|       Y d}~yd}~ww xY w)zCBest-effort parse without raising; returns None on malformed input.z&Skipping malformed component entry: %sN)r$   r-   	Exceptionr   debug)r*   es     r   parse_or_nonezComponentConfig.parse_or_nonev   s:    	",,V44 	MMBAF	s    	A ;A c           	     v   | j                   yt        j                  | j                          || j                   z  j                         }|j	                         r|j                         s)t        d| j                    d| j                   d| d      t        j                  ||j                         d       |S )a@  Resolve and security-check the component's asset root directory.

        Parameters
        ----------
        package_root : Path
            The root directory of the installed component package.

        Returns
        -------
        Path | None
            Absolute, resolved path to the asset directory, or ``None`` if
            ``asset_dir`` is not declared.

        Raises
        ------
        StreamlitComponentRegistryError
            If the declared directory does not exist, is not a directory, or
            resolves outside of ``package_root``.
        NzDeclared asset_dir 'z' for component 'z;' does not exist or is not a directory under package root 'z'.r%   )abs_pathrootkind)	r%   r   validate_path_securityresolveexistsis_dirr   r   ensure_within_root)selfpackage_root
asset_roots      r   resolve_asset_rootz"ComponentConfig.resolve_asset_root   s    ( >>! 	11$..A"T^^3<<>
  "**;*;*=1&t~~&66G		{ SLLX>Y[]  	--%%'	
 r   )r*   dict[str, Any]returnr$   )r*   r@   rA   zComponentConfig | None)r=   r   rA   Path | None)
r   r   r   r    r!   r%   staticmethodr-   r2   r?   r"   r   r   r$   r$   J   sC     I Iz 
 
8  )r   r$   c                   | j                   j                         }d| j                  v r| j                  d   j                         nd}d|v ryd|v ry	 | j                  j                  d      xs g }|D ]  }|sd|j                         v s y 	 |j                  d      S # t        $ r }t
        j                  d|       Y d}~5d}~ww xY w)	a  Check if a package is likely to contain streamlit components before
    expensive operations.

    This early filter reduces the number of packages that need file I/O
    operations from potentially hundreds down to just a few candidates.

    Parameters
    ----------
    dist : importlib.metadata.Distribution
        The package distribution to check.

    Returns
    -------
    bool
        True if the package might contain streamlit components, False otherwise.
    Summary 	streamlitTzRequires-DistzFFailed to parse package metadata for streamlit component detection: %sN)z
streamlit-
streamlit_zst-st_)r   lowermetadataget_allr/   r   r0   
startswith)distr   summaryrequires_distrequirementr1   s         r   &_is_likely_streamlit_component_packagerR      s    ( 99??D2;t}}2LdmmI&,,.RTG d g

--o>D"( 	K{k.?.?.AA	 ??EFF  
TVW	
 	

s*   &B" :B" B" B" "	C+CCc                v    t        | j                        t        t        fdfD ]  } ||       }||c S  y)a  Find ``pyproject.toml`` for a package.

    Handles both regular and editable installs. The function uses increasingly
    permissive strategies to locate the file while validating that the file
    belongs to the given distribution.

    Parameters
    ----------
    dist : importlib.metadata.Distribution
        The package distribution to find pyproject.toml for.

    Returns
    -------
    Path | None
        Path to the ``pyproject.toml`` file if found, otherwise ``None``.
    c                    t        |       S N)_pyproject_via_import_spec)dpackage_names    r   <lambda>z._find_package_pyproject_toml.<locals>.<lambda>   s    ,Q= r   N)r   r   _pyproject_via_read_text_pyproject_via_dist_files)rN   finderresultrX   s      @r   _find_package_pyproject_tomlr^      sJ    " +4995L 	!!= 
 M r   c                   t        | j                        }	 t        | d      r| j                  d      }|r| j                  r| j                  D ]  }dt        |      v sdt        |      v s	 t        t        | j                  |                  }|j                  }||j                  fD ]5  }|dz  }|j                         st        || j                  |      s1|c c S   y y# t        $ r Y w xY w# t        $ r Y yw xY w)zLocate pyproject.toml using the distribution's read_text + nearby files.

    This works for many types of installations including some editable ones.
    	read_textpyproject.toml__init__.pyz.pyN)r   r   hasattrr`   filesr   r   locate_fileparentr9   _validate_pyproject_for_packager/   )rN   rX   pyproject_contentfile	file_pathcurrent_dir
search_dirpyproject_paths           r   rZ   rZ      s   
 +4995L4% $/? @ TZZ !JJ %D$D	1Uc$i5G%(,S1A1A$1G-H(II*3*:*:K/:K<N<N.O 
:
1;>N1N$2$9$9$;(G(6(,		(4)& ,:$9
: "
 1%0 	  ) %$% sU   AC5 -AC&C&C&C5 !C&"C5 $C5 &	C2/C5 1C22C5 5	D Dc                N   t        | j                        }t        | dd      }|sy|D ]l  }t        |dd      dk(  st        |      j	                  d      s.	 t        t        | j                  |                  }t        || j                  |      r|c S n y# t        $ r Y {w xY w)z?Locate pyproject.toml by scanning the distribution's file list.rd   Nr   ra   )	r   r   getattrr   endswithr   re   rg   r/   )rN   rX   rd   ri   rm   s        r   r[   r[   $  s    *4995LD'4(E 4&*::c$i>P>P?
	!%c$*:*:4*@&A!B2"II 
 *)   s   ;B	B$#B$c                D   	 t         j                  j                  |      }|ro|j                  rct	        |j                        j
                  }||j
                  fD ]3  }|dz  }|j                         st        || j                  |      s1|c S  y# t        $ r Y yw xY w)zLocate pyproject.toml by resolving the import spec and checking nearby.

    For editable installs, try the package directory and its parent only.
    ra   N)
	importlibutil	find_specoriginr   rf   r9   rg   r   r/   )rN   rX   specpackage_dirrl   rm   s         r   rV   rV   ;  s    ~~''5DKKt{{+22K*K,>,>? *
!+.>!>!((*/N"II 0
 *)*   s$   A2B 5B B B 	BBc                   	 t        | d      5 }t        j                  |      }ddd       d}dv rd|d   v r|d   d   }|s d|v rd|d   v rd|d   d   v r|d   d   d   }|rEt        j                  |      }t        j                  |      }t        j                  |      }|||fv S y	# 1 sw Y   xY w# t
        $ r"}	t        j                  d
| ||	       Y d}	~	y	d}	~	ww xY w)a  Validate that a ``pyproject.toml`` file belongs to the specified package.

    Parameters
    ----------
    pyproject_path : Path
        Path to the pyproject.toml file to validate.
    dist_name : str
        The distribution name (e.g., "streamlit-bokeh").
    package_name : str
        The package name (e.g., "streamlit_bokeh").

    Returns
    -------
    bool
        True if the file belongs to this package, False otherwise.
    utf-8encodingNprojectr   tool
setuptoolszpackage-nameFz0Error validating pyproject.toml at %s for %s: %s)opentomlloadpackaging_utilscanonicalize_namer/   r   r0   )
rm   r   rX   fpyproject_dataproject_namecanonical_projectcanonical_distcanonical_packager1   s
             r   rg   rg   S  s$   &+.73 	*q!YYq\N	*  &6^I5N+N))4V<L .(v 66"nV&<\&JJ *&1,?OL  !0 A A, O,>>yIN / A A, O %9J(KKK C	* 	*F  >		
 s.   B8 B,BB8 ,B51B8 8	C#CC#c                    	 t        | d      5 }t        j                  |      cddd       S # 1 sw Y   yxY w# t        $ r!}t        j                  d| |       Y d}~yd}~ww xY w)zJLoad and parse a pyproject.toml, returning parsed data or None on failure.ry   rz   Nz(Failed to parse pyproject.toml at %s: %s)r   r   r   r/   r   r0   )rm   r   r1   s      r   _load_pyprojectr     sW    .73 	 q99Q<	  	  	  @.RSTs)   : .	: 7: : 	A$AA$c                   | j                  di       j                  di       j                  d      }|sy|j                  d      }t        |t              sy|D cg c]  }t        |t              s| }}|sy|S c c}w )zGExtract raw component dicts from pyproject data; return None if absent.r}   rG   	componentNr   )r'   r(   listdict)r   streamlit_componentraw_componentsitemr]   s        r   _extract_componentsr     s     	62&**;;??L  (,,\:Nnd+ ($:dD+A$F $ M$s   A<1A<c                >   d}	 t         j                  j                  |      }|r+|j                  rt	        |j                        j
                  }t        | dd      }|sV|rT|D ]O  }|t        |      v sdt        |      v s	 t	        t        | j                  |                  }|j
                  } n |s|j
                  }|S # t        $ r!}t        j                  d||       Y d}~d}~ww xY w# t        $ r!}t        j                  d||       Y d}~d}~ww xY w)z2Resolve the package root directory with fallbacks.Nz9Failed to resolve package root via import spec for %s: %srd   rb   z8Failed to resolve package root via dist files for %s: %s)rr   rs   rt   ru   r   rf   r/   r   r0   ro   r   re   )	rN   rX   rm   r=   rv   r1   rd   ri   	init_paths	            r   _resolve_package_rootr     s    !%L	
~~''5DKK,33L D'4(EE 	Ds4y(]c$i-G	 $S)9)9$)?%@ AI#,#3#3L	 %,,3  
G	
 	

 ! MMR$ s0   AC /C2	C/C**C/2	D;DDc                    | j                  di       }|j                  d      xs |j                  }|j                  d      xs |j                  xs d}||fS )z4Derive project name and version with safe fallbacks.r|   r   r   z0.0.0)r'   r   r   )r   rN   project_tablederived_namederived_versions        r   _derive_project_metadatar     sX     #&&y"5M $$V,9		L#''	2MdllMgO((r   c           	        	 t        |       }|syt        |      }|yt        |      }|syt        | j                        }t        | ||      }t        ||       \  }}|D cg c]  }t        j                  |      x}	|	 }
}|
syt        |||
      }||fS c c}w # t        $ r,}t        j                  dt        | dd      |       Y d}~yd}~ww xY w)a  Process a single package to extract component manifest.

    This function is designed to be called from a thread pool for parallel processing.

    Parameters
    ----------
    dist : importlib.metadata.Distribution
        The package distribution to process.

    Returns
    -------
    tuple[ComponentManifest, Path] | None
        The manifest and package root if found, otherwise ``None``.
    N)r   r   r   z/Unexpected error processing distribution %s: %sr   z	<unknown>)r^   r   r   r   r   r   r   r$   r2   r   r/   r   r0   ro   )rN   rm   r   r   rX   r=   r   r   compparsedparsed_componentsmanifestr1   s                r   _process_single_packager     s    ")5d;(8!,^<.tyy9,T<P(@QU(V%o '4
)77==J 4
 4
 !$#(
 ,''4
"  =D&+.	

 s>   B B B 5B ! BB B B 	C&"CCc                   g }t        t        j                  j                               }|s|S |D cg c]  }t	        |      r| }}t
        j                  dt        |      t        |             |s|S | %t        dt        j                         xs ddz         } t        | t        |      d      } t
        j                  dt        |      |        t        |       5 }|D ci c]#  }|j                  t        |      |j                  % }}t        |      D ]&  }|j!                         }|s|j#                  |       ( 	 ddd       t
        j                  d	t        |             |S c c}w c c}w # 1 sw Y   4xY w)
a%  Scan installed packages for Streamlit component metadata.

    Uses parallel processing to improve performance in environments with many
    installed packages. Applies early filtering to only check packages likely to
    contain streamlit components.

    Parameters
    ----------
    max_workers : int or None
        Maximum number of worker threads. If None, uses min(32, (os.cpu_count()
        or 1) + 4).

    Returns
    -------
    list[tuple[ComponentManifest, Path]]
        List of tuples of manifests and their package root paths.
    zAFiltered %d packages down to %d candidates for component scanningN             zNScanning %d candidate packages for component manifests using %d worker threads)max_workersz"Found %d component manifests total)r   rr   rK   distributionsrR   r   r0   lenminos	cpu_countr   submitr   r   r   r]   append)	r   	manifestsall_distributionsrN   candidate_distributionsexecutorfuture_to_distfuturer]   s	            r   scan_component_manifestsr     s   ( 79I Y//==?@
 &1$7 	  MMK#$ # "r||~2a78 S012K MMX#$ 
	4 ) 0
 OO3T:DIIE
 
 #>2 	)F]]_F  (	)) MM6IG[B
) )s)   E+E5#(E0"E5.E50E55E>)r   r   rA   r   )rN   importlib.metadata.DistributionrA   bool)rN   r   rA   rB   )rN   r   rX   r   rA   rB   )rm   r   r   r   rX   r   rA   r   )rm   r   rA   zdict[str, Any] | None)r   r@   rA   zlist[dict[str, Any]] | None)rN   r   rX   r   rm   r   rA   r   )r   r@   rN   r   rA   ztuple[str, str])rN   r   rA   z%tuple[ComponentManifest, Path] | NonerU   )r   z
int | NonerA   z$list[tuple[ComponentManifest, Path]])-r    
__future__r   importlib.metadatarr   importlib.utilr   concurrent.futuresr   r   dataclassesr   pathlibr   typingr   r	   r   	packagingr
   r   ,streamlit.components.v2.component_path_utilsr   streamlit.errorsr   streamlit.loggerr   r   r   r!   r   r   r$   rR   r^   rZ   r[   rV   rg   r   r   r   r   r   r   r"   r   r   <module>r      sj   #   	 ? !    . K < 'H% %'( & & & ] ] ]@/G
)/G	/Gd@$N.
)9<0>>%(>8;>	>B&"
)"9<"NR"	"J)")*I)):
):*:| #JJ)Jr   