
    ^j)              	       T   d Z ddlZddlZddlZddl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mZ dedeeef   fd	Zd
edededee   fdZdededee   fdZdedee   fdZddedee   dee   fdZdedefdZdedee   fdZe	deded   fd       ZdedefdZdedefdZy)a  Package and filesystem helpers for pyDeprecate's CLI.

Internal utilities that resolve a package's version, locate package directories, and manage ``sys.path`` for scanning.

Version resolution uses a local ``pyproject.toml`` (development checkout) when available, falling back to installed
distribution metadata via ``importlib.metadata``.

TOML parsing uses ``tomllib`` (stdlib on Python 3.11+) or ``tomli`` (backport, via ``pip install 'pyDeprecate[audit]'``
on Python 3.10). When not available the helpers return ``None`` and callers fall back to ``importlib.metadata``.

This module is private (no ``__all__``); its surface may change without notice.

    N)	Generator)contextmanager)Path)AnyOptionalpathreturnc                     	 	 ddl }t        | d      5 }t	        |j                  |            cddd       S # t        $ r ddl}Y ?w xY w# 1 sw Y   yxY w# t        $ r i cY S w xY w)a  Load a TOML file using ``tomllib`` (Python 3.11+) or ``tomli`` (Python 3.10 backport).

    Returns an empty dict on any failure (missing library, parse error, IO).

    Examples:
        >>> import os, tempfile
        >>> with tempfile.NamedTemporaryFile(suffix=".toml", mode="w", delete=False) as f:
        ...     _ = f.write('[project]\nname = "mypkg"\nversion = "1.2.3"\n')
        ...     name = f.name
        >>> _load_toml(name).get("project", {}).get("version")
        '1.2.3'
        >>> os.unlink(name)

    r   Nrb)tomllibImportErrortomliopendictload	Exception)r   r   fhs      Y/var/www/ramen.bs-engineer-server.com/venv/lib/python3.12/site-packages/deprecate/_pkg.py
_load_tomlr      so    		$ $ 	*R()	* 	*  	$#	$	* 	* 	sH   7 A A
	A AA AA 
AA A A$#A$pkg_name	scan_pathpyproject_dirc                 t   t         j                  j                  |      rt         j                  j                  |      n;t         j                  j	                  t         j                  j                  |            }t        t        j                        }g }|t         j                  j	                  |      |t         j                  j                  |d      fD ][  }|st         j                  j                  |      s&||vs+|j                  |       t        j                  j                  d|       ] 	 t        j                  |       }t        |dd      }t        |t              r|nd	 |t        j                  dd S # t        $ r Y |t        j                  dd yw xY w# |t        j                  dd w xY w)ax  Import *pkg_name* and return its ``__version__``, or ``None`` on any failure.

    Adds candidate import roots (scan dir, its parent, pyproject dir, ``src/``
    sub-dir) to ``sys.path`` before importing, then restores the original
    ``sys.path`` unconditionally via ``finally``.

    Args:
        pkg_name: Importable package name declared in ``[project].name``.
        scan_path: File-system path originally passed to the CLI (used to
            locate the package root).
        pyproject_dir: Directory containing the ``pyproject.toml`` file.

    Returns:
        Version string from ``pkg.__version__`` or ``None``.

    srcr   __version__N)osr   isdirabspathdirnamelistsysjoinappendinsert	importlibimport_modulegetattr
isinstancestrr   )	r   r   r   	scan_rootoriginaladdedrootmoduleversions	            r   _version_from_dynamicr0   5   s:   " /1ggmmI.F	*BGGOO\^\c\c\k\klu\vLwICHH~HEBGGOOI6rww||TachGij %BGGMM$'D,=LLHHOOAt$%((2&-6$Wc2w<    s$   65F   	F	F" FF" "F7	toml_pathc                 L   t        |       }|j                  di       }|sy|j                  d      }t        |t              r|S d|j                  dg       v rM|j                  d      }t        |t              r,|r*t	        ||t
        j                  j                  |             S y)a  Extract ``[project].version`` from *toml_path*, or ``None``.

    Falls back to importing the package for ``dynamic = ["version"]`` projects.

    Args:
        toml_path: Absolute path to a ``pyproject.toml`` file.
        scan_path: Original file-system path passed to the CLI (forwarded to
            :func:`_version_from_dynamic` for import-root resolution).

    Returns:
        Version string or ``None`` when not resolvable.

    projectNr/   dynamicname)r   getr(   r)   r0   r   r   r   )r1   r   datar3   r/   r   s         r   _version_from_tomlr8   W   s     i Dhhy"%Gkk)$G'3GKK	2..;;v&h$(9bggooi>XYY    c                    t         j                  j                  |       }t         j                  j                  |      st         j                  j	                  |      }t        d      D ]{  }t         j                  j                  |d      }t         j                  j                  |      rt        ||       }||c S t         j                  j	                  |      }||k(  r y|}} y)u  Return ``[project].version`` from the nearest ``pyproject.toml`` up to 2 levels above *path*.

    Searches the directory itself, its parent, and its grandparent — stops
    as soon as a resolvable version is found or the limit is reached.

    Args:
        path: File-system path to start the upward search from.

    Returns:
        Version string or ``None`` when not found within 2 levels.

       zpyproject.tomlN)	r   r   r   r   r   ranger"   isfiler8   )r   	candidate_r1   r/   parents         r   _read_pyproject_versionrA   v   s     %I77==#GGOOI.	1X 	GGLL,<=	77>>)$(D9G"+Y 		 r9   module_namec                     |t        |      }||S 	 t        j                  j                  |       S # t        $ r Y yw xY w)u  Return the version for *module_name*, or ``None`` on any failure.

    Priority order:

    1. ``[project].version`` in the nearest ``pyproject.toml`` above *path* (reflects
       local development checkouts whose version may differ from the installed dist).
    2. ``importlib.metadata.version(module_name)`` — the installed distribution.

    Args:
        module_name: Importable package name whose installed version to look up.
        path: Optional file-system path used to locate a local ``pyproject.toml``.
            When provided, the local file takes precedence over installed metadata.

    Returns:
        Version string (e.g. ``"1.2.3"``) or ``None`` when the package is not
        installed or the version cannot be determined.

    N)rA   r%   metadatar/   r   )rB   r   	local_vers      r   _auto_detect_versionrF      sN    & +D1	 !!))+66 s   2 	>>pthc                 (    | dz  j                         S )zSReturn True if *pth* is an importable package directory (contains ``__init__.py``).z__init__.py)exists)rG   s    r   _is_package_dirrJ      s    -''))r9   c                 2   | dz  }|j                         r>|j                         D cg c]!  }|j                         st        |      s |# }}|r|S | j                         D cg c]!  }|j                         st        |      s |# c}S c c}w c c}w )a  Return package sub-directories of *pth*, checking ``src/`` when none found directly.

    Resolution order:
    1. Immediate child directories of ``pth/src/`` that contain ``__init__.py``
       (standard ``src/``-layout projects take precedence).
    2. If none, immediate child directories of *pth* that contain ``__init__.py``.

    Args:
        pth: Directory to search.

    Returns:
        List of :class:`~pathlib.Path` objects for each package directory found.

    r   )is_diriterdirrJ   )rG   src_dircin_srcs       r   _find_child_packagesrQ      s{     EkG~~$__.T!((*QRAS!TTM{{}J!
q7IAJJ U Ks"   BB
B&B<BB)NNNc              #     K   t        |       j                         }t        t        j                        }|j                         rt        |      rt        |j                        }nO|dz  }|j                         xr  t        d |j                         D              }|rt        |      n
t        |      }t        j                  j                  d|       	 t        j                         5  t        j                  dt        d       t        j                  dt         d       d ddd       |t        j                  dd y# 1 sw Y   xY w# |t        j                  dd w xY ww)aP  Context manager that prepends the import root to ``sys.path`` and restores it after scanning.

    For package directories (containing ``__init__.py``), inserts the parent directory so the package name resolves as
    an importable module. For plain directories, inserts the directory itself. Importable module name strings are passed
    through unchanged.

    Warning:
        Not thread-safe. ``sys.path`` is a process-global list; concurrent use from multiple threads will corrupt
        the restored state. Each scan should run in a dedicated process or be serialized via a lock.

    r   c              3   V   K   | ]!  }|j                         xr t        |       # y wN)rL   rJ   .0rO   s     r   	<genexpr>z$_managed_sys_path.<locals>.<genexpr>   s$     -kTUahhj.O_Q=O.O-ks   ')r   ignorezdeprecate.*)categoryr.   N)r   resolver    r!   r   rL   rJ   r)   r@   anyrM   r$   warningscatch_warningsfilterwarningsDeprecationWarningFutureWarning)r   abs_pathr+   import_rootrN   rP   s         r   _managed_sys_pathrc      s     $Z'')HCHH~H8$hoo.K&G^^%k#-kY`YhYhYj-k*kF*0#g,c(mK;'$$& 	##H7IR_`##H}][	
 	 	
 s6   CE'E *=E'E /E'EE E$$E'c                    t        |       }|j                         rt        d| d      |j                         rt	        |      r|j                         j                  S t        |      }t        |      dk(  r|d   j                  S t        |      dkD  r3dj                  t        d |D                    }t        d| d| d	      t        d
| d      | S )a  Convert a filesystem path to an importable module name.

    Accepts a package directory (with ``__init__.py``) or an importable module
    name string. Plain directories and individual ``.py`` files are not supported
    because ``validate_deprecation_expiry`` and ``validate_deprecation_chains``
    require an importable module name, not a filesystem path.

    Args:
        path: Package directory path or importable module name string.

    Returns:
        Importable module name string.

    Raises:
        ValueError: If path is a plain directory without ``__init__.py``, a ``.py``
            file, or cannot be resolved to an importable module name.

    zFile paths are not supported: z@. Pass an importable module/package name or a directory instead.   r   z, c              3   4   K   | ]  }|j                     y wrT   )r5   rU   s     r   rW   z'_resolve_module_name.<locals>.<genexpr>  s     $@QVV$@s   z
Directory z contains multiple packages (z3). Pass the specific package sub-directory instead.zVPlain directories without '__init__.py' are not supported for expiry or chain checks: za. Use an importable package layout with '__init__.py', or pass an importable module name instead.)r   is_file
ValueErrorrL   rJ   rZ   r5   rQ   lenr"   sorted)r   rG   
child_pkgsnamess       r   _resolve_module_namerm      s    & t*C
{{},TH4tu
 	
 zz|3;;=%%%)#.
z?aa=%%%z?QIIf$@Z$@@AETH$A% IC C  deidl mn n
 	
 Kr9   c                 <    	 t        |       S # t        $ r | cY S w xY w)zLReturn the importable module name for *path*, falling back to *path* itself.)rm   rh   )r   s    r   _safe_module_namero     s&    #D)) s   
 rT   )__doc__r%   importlib.metadatar   r!   r\   collections.abcr   
contextlibr   pathlibr   typingr   r   r)   r   r   r0   r8   rA   rF   boolrJ   r    rQ   rc   rm   ro    r9   r   <module>rx      s?     	 
  % %   S T#s(^ 6C C  PXY\P] D# # (3- ># (3- :c # (SV- :* *$ *
Kd KtDz K. C I.>$?  >)s )s )XC C r9   