
    5iI4                       U d Z ddlmZ ddlZddlmZ ddlmZmZ ddl	m
Z
 erddlmZ  e
e      Zded	<   d
Zded<   dZded<   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 dZddgZy)!a  App discovery utilities for detecting ASGI app instances in scripts.

This module provides functions to discover if a Python script contains an
ASGI application instance (like st.App, FastAPI, or Starlette), enabling
the CLI to auto-detect whether to run in traditional mode or ASGI mode.

By design, this supports not only Streamlit's st.App but also other ASGI
frameworks like FastAPI and Starlette. This allows `streamlit run` to serve
as a unified entry point for any ASGI app, providing a consistent developer
experience for projects that combine Streamlit with other frameworks or use
ASGI apps directly.

The detection uses AST (Abstract Syntax Tree) parsing to safely analyze
the source code without executing it.
    )annotationsN)	dataclass)TYPE_CHECKINGFinal)
get_logger)Pathr   _LOGGER)appstreamlit_appzFinal[tuple[str, ...]]_PREFERRED_APP_NAMES)zstreamlit.starlette.Appz"streamlit.web.server.starlette.Appz0streamlit.web.server.starlette.starlette_app.Appzfastapi.FastAPIzfastapi.applications.FastAPIz starlette.applications.Starlette_KNOWN_ASGI_APP_CLASSESc                  0    e Zd ZU dZded<   ded<   ded<   y)AppDiscoveryResulta#  Result of ASGI app discovery.

    Attributes
    ----------
    is_asgi_app
        True if the script contains an ASGI app instance.
    app_name
        The name of the app instance variable (e.g., "app").
    import_string
        The import string for uvicorn (e.g., "module:app").
    boolis_asgi_app
str | Noneapp_nameimport_stringN)__name__
__module____qualname____doc____annotations__     l/home/obispo/Crisostomo_bridge/mision_env/lib/python3.12/site-packages/streamlit/web/server/app_discovery.pyr   r   E   s    
 r   r   c                j   | j                   }g }t        |t        j                        rB|j	                  |j
                         |j                  }t        |t        j                        rBt        |t        j                        r/|j	                  |j                         t        t        |            S y)a  Extract the name parts from a Call node's func attribute.

    For example:
    - `App(...)` returns ("App",)
    - `st.App(...)` returns ("st", "App")
    - `streamlit.starlette.App(...)` returns ("streamlit", "starlette", "App")

    Parameters
    ----------
    node
        An AST Call node.

    Returns
    -------
    tuple[str, ...] | None
        A tuple of name parts, or None if the call target is not a simple
        name or attribute chain.
    N)func
isinstanceast	AttributeappendattrvalueNameidtuplereversed)noder   partss      r   _get_call_name_partsr+   X   s{    & 99DE
T3==
)TYYzz T3==
) $!TWWXe_%%r   c                   i }t        j                  |       D ]  }t        |t         j                        r;|j                  D ]+  }|j
                  xs |j                  }|j                  ||<   - Xt        |t         j                        ss|j                  s|j                  D ]:  }|j
                  xs |j                  }|j                   d|j                   ||<   <  |S )u_  Extract import mappings from an AST.

    Builds a mapping from local names to their fully qualified module paths.

    For example:
    - `from streamlit.starlette import App` → {"App": "streamlit.starlette.App"}
    - `from streamlit import starlette` → {"starlette": "streamlit.starlette"}
    - `import streamlit as st` → {"st": "streamlit"}
    - `import fastapi` → {"fastapi": "fastapi"}

    Parameters
    ----------
    tree
        The parsed AST of a Python module.

    Returns
    -------
    dict[str, str]
        A mapping from local names to their fully qualified module paths.
    .)	r    walkr   Importnamesasnamename
ImportFrommodule)treeimportsr)   alias
local_names        r   _extract_importsr9   y   s    * !G DdCJJ' 1"\\7UZZ
&+jj
#1 cnn-$++ D"\\7UZZ
)-Quzzl&C
#DD Nr   c                    | sy| d   }| dd }||v r||   }|r| ddj                  |       S |S dj                  |       S )u  Resolve a call's name parts to a fully qualified module path.

    Uses the import mapping to resolve the first part of the call chain,
    then appends any remaining parts.

    For example, with imports {"App": "streamlit.starlette.App"}:
    - ("App",) → "streamlit.starlette.App"

    With imports {"st": "streamlit"}:
    - ("st", "starlette", "App") → "streamlit.starlette.App"

    Parameters
    ----------
    parts
        The name parts from a Call node (e.g., ("st", "App")).
    imports
        The import mapping from _extract_imports.

    Returns
    -------
    str | None
        The fully qualified module path, or None if resolution fails.
    Nr      r-   )join)r*   r6   
first_partremaining_parts	base_paths        r   _resolve_call_to_module_pathr@      sd    4 qJABiOWJ'	[#((?";!<==
 88E?r   c                L    t        |       }|yt        ||      }|y|t        v S )a  Check if a Call node represents a known ASGI app constructor.

    This function resolves the call to its fully qualified module path
    using the import mapping, then checks if it matches any known
    ASGI app class.

    Parameters
    ----------
    node
        An AST Call node.
    imports
        The import mapping from _extract_imports.

    Returns
    -------
    bool
        True if the call is a known ASGI app constructor.
    F)r+   r@   r   )r)   r6   r*   resolved_paths       r   _is_asgi_app_callrC      s7    & !&E}0@M333r   c                    | j                         }|j                         r%|j                  dk(  r|j                  j                  S |j                  S )a  Convert a file path to a module import string.

    Since `streamlit run` adds the script's directory to sys.path via
    _fix_sys_path, the module string should just be the script's stem,
    not a fully qualified package path.

    Parameters
    ----------
    path
        Path to the Python file.

    Returns
    -------
    str
        The module string suitable for uvicorn (e.g., "myapp").
    __init__)resolveis_filestemparent)pathresolveds     r   _get_module_string_from_pathrL      sB    " ||~H hmmz9###==r   c                p   	 t        j                  |       }t        |      }i }t        j                  |      D ]K  }t        |t         j                        rt        |j                  t         j                        r\t        |j                  |      rF|j                  D ]6  }t        |t         j                        s|j                  ||j                  <   8 t        |t         j                         s|j                  st        |j                  t         j                        st        |j                  |      st        |j"                  t         j                        s)|j                  ||j"                  j                  <   N |S # t        $ r"}t        j	                  d|       i cY d}~S d}~ww xY w)a  Find all variable assignments to ASGI app constructors in source code.

    This function parses the source code, extracts import statements to
    understand the module context, then finds assignments to known ASGI
    app constructors.

    Parameters
    ----------
    source
        Python source code to analyze.

    Returns
    -------
    dict[str, int]
        A mapping of variable names to their line numbers where ASGI app
        instances are assigned.
    zFailed to parse source: %sN)r    parseSyntaxErrorr	   debugr9   r.   r   Assignr$   CallrC   targetsr%   linenor&   	AnnAssigntarget)sourcer5   er6   app_assignmentsr)   rV   s          r   _find_asgi_app_assignmentsrZ     s-   $yy  t$G&(O : tSZZ(4::sxx0!$**g6,, =fchh/15OFII.= tS]]+

4::sxx0!$**g64;;1.2kkODKKNN+':* =  2A6	s   F
 
	F5F0*F50F5c                ^   | j                         s$t        j                  d|        t        ddd      S 	 | j	                  d      }t        |      }|s$t        j                  d|        t        ddd      S t        |       }|rV||v r.t        j                  d	||||          t        d
|| d|       S t        j                  d|       t        ddd      S t        D ]6  }||v st        j                  d||||          t        d
|| d|       c S  t        |j                         d       }t        j                  d||d   |d          t        d
|d   | d|d          S # t
        t        f$ r/}t        j                  d| |       t        ddd      cY d}~S d}~ww xY w)ai  Discover if a Python file contains an ASGI app instance using AST parsing.

    This function safely analyzes the source code without executing it.
    It tracks import statements to verify that detected App classes actually
    come from known ASGI frameworks (streamlit, fastapi, starlette), preventing
    false positives from custom classes with the same name.

    Supported import patterns:
    - `from streamlit.starlette import App`
    - `import streamlit` (for `streamlit.starlette.App`)
    - `from fastapi import FastAPI`
    - `from starlette.applications import Starlette`

    The app variable can have any name (e.g., `app`, `my_dashboard`, `server`).
    Preferred names checked first: "app", "streamlit_app".

    Parameters
    ----------
    path
        Path to the Python script to check.
    app_name
        Optional specific variable name to look for. If provided, only that
        name is checked. If not provided, checks preferred names first
        ("app", "streamlit_app"), then falls back to any
        discovered ASGI app.

    Returns
    -------
    AppDiscoveryResult
        Discovery result indicating whether an ASGI app was found and how
        to import it.

    Examples
    --------
    >>> result = discover_asgi_app(Path("streamlit_app.py"))
    >>> if result.is_asgi_app:
    ...     print(f"Found ASGI app: {result.import_string}")
    zPath does not exist: %sFN)r   r   r   zutf-8)encodingzFailed to read file %s: %sz#No ASGI app assignments found in %sz!Found ASGI app at %s:%s (line %d)T:z No ASGI app found with name '%s'z1Found ASGI app at %s:%s (preferred name, line %d)c                    | d   S )Nr;   r   )xs    r   <lambda>z#discover_asgi_app.<locals>.<lambda>  s
    1Q4 r   )keyz+Found ASGI app at %s:%s (fallback, line %d)r   r;   )existsr	   rP   r   	read_textOSErrorUnicodeDecodeErrorrZ   rL   r   minitems)rJ   r   rW   rX   rY   
module_strpreferred_name	first_apps           r   discover_asgi_apprk   :  s   T ;;=/6!edRVWWX1
 18O;TB!edRVWW-d3J &MM3)	 & !!+AhZ8 
 	8(C!edRVWW / _,MMC/	 & '!+An-=>  O))+@IMM5!!	 1#Ail^4 i '( X2D!<!edRVWWXs   E. .F,=$F'!F,'F,rk   )r)   ast.Callreturnztuple[str, ...] | None)r5   zast.ASTrm   dict[str, str])r*   ztuple[str, ...]r6   rn   rm   r   )r)   rl   r6   rn   rm   r   )rJ   r   rm   str)rW   ro   rm   zdict[str, int])N)rJ   r   r   r   rm   r   )r   
__future__r   r    dataclassesr   typingr   r   streamlit.loggerr   pathlibr   r   r	   r   r   r   r   r+   r9   r@   rC   rL   rZ   rk   __all__r   r   r   <module>rv      s     # 
 ! ' 'H% % 0H , G
3 / 
   $B$N**%3**Z4<42n  h
hh hV  !4
5r   