+
    2j)                    Z   R t ^ RIHt ^ RIt^ RIHt ^ RIHtHt ^ RI	t	^RI
Ht ^RIHt ^RIHtHt ^RIHt ]'       d   ^ R	IHt ^ R
IHt . ROt]P0                  ! ]4      t ! R R]4      tRtR R ltR R lt]! RRR7       ! R R4      4       t R R lt!R R lt"R R lt#R R lt$R# )a7  Workspace discovery for member ``pyproject.toml`` files.

A "workspace" is a table of members declared by a project: either
``[tool.nab.workspace]`` in its ``pyproject.toml`` or ``[workspace]`` in
the project-dir ``nab.toml``.  Every member is synthesised into a
:class:`~nab_python.provider.LocalSource`.  The provider then prefers
those local sources over PyPI by canonical name, so a member package
resolves against its in-tree source instead of being fetched from the
index.

A project that declares no workspace of its own walks up to an ancestor
project file that does, in either spelling, so ``nab lock`` invoked
against a member still resolves against the root's members.

Members are listed literally; globs are refused with an error.  Users
coming from other tools that allow globs get a clear migration
message.
)annotationsN)	dataclass)TYPE_CHECKINGAny)tool_nab_section)canonicalize_name)
path_stateresolve_path)LocalSource)Iterable)PathWorkspaceConfigWorkspaceDiscoveryErrorc                      ] tR t^3tRtRtR# )r   z?Raised when a workspace member or root is structurally invalid. N)__name__
__module____qualname____firstlineno____doc____static_attributes__r       _/home/user/billing-ledger-validation/.venv/lib/python3.14/site-packages/nab_python/workspace.pyr   r   3   s    Ir   znab.tomlc                    V ^8  d   QhRRRR/# )   	pyprojectr   returndict[str, Any]r   )formats   "r   __annotate__r   :   s     4 4 4. 4r   c                P    V P                  R4      ;_uu_ 4       p\        P                  ! V4      uuRRR4       #   + '       g   i     R# ; i  \        \        P                  3 d   pT  RT 2p\        T4      ThRp?i\         d   pRT  RT 2p\        T4      ThRp?ii ; i)a  Parse ``pyproject``, raising :class:`WorkspaceDiscoveryError` on a bad read.

:func:`discover_workspace_root` swallows read and parse errors while
walking, but a chosen root and its declared members must parse, so an
unreadable or malformed file here is a hard error.
rbNz is not valid TOML: zcannot read : )opentomliloadUnicodeDecodeErrorTOMLDecodeErrorr   OSError)r   fexcmsgs   &   r   _load_member_tomlr,   :   s    4^^D!!Q::a= "!!! 5 56 4/u5%c*3 4YKr#/%c*34sC   A <
A A	A A B%+A>>B%B%B  B%c               $    V ^8  d   QhRRRRRR/# )r   datar   project_filer   r   ztuple[Any, str]r   )r   s   "r   r   r   L   s!     ' ' 't ' 'r   c                    VP                   \        8X  d   V P                  R4      R3# \        V 4      pRp\	        V\
        4      '       g   RV3# VP                  R4      V3# )aq  Return the workspace table ``project_file`` declares, and its name.

A ``pyproject.toml`` spells it ``[tool.nab.workspace]``; the
project-dir ``nab.toml`` spells it as a top-level ``[workspace]``.
The value is returned unvalidated, so a malformed table still counts
as a declaration and errors when its members are read.  ``None``
means the file declares no workspace.
	workspacez[workspace]z[tool.nab.workspace]N)name_PROJECT_TOMLgetr   
isinstancedict)r.   r/   nablabels   &&  r   _workspace_declarationr9   L   s`     M)xx$m33
4
 C"Ec4  U{77;&&r   T)frozenslotsc                  &    ] tR t^^t$ RtR]R&   RtR# )r   zParsed workspace table.

``members`` is the literal list of paths declared at the workspace
root.  No globs, no path resolution; those happen later in
:func:`workspace_local_sources`.
ztuple[str, ...]membersr   N)r   r   r   r   r   __annotations__r   r   r   r   r   r   ^   s     r   c                    V ^8  d   QhRRRR/# )r   member_pyprojectr   r   zPath | Noner   )r   s   "r   r   r   j   s      d { r   c           	        V P                  4       P                  pV.VP                  O Fq  pVR,          V\        ,          3 FT  p VP	                  R4      ;_uu_ 4       p\
        P                  ! V4      pRRR4       \        XT4      w  rgTf   KP  Tu u # 	  Ks  	  R#   + '       g   i     L2; i  \        \        \
        P                  3 d     K  i ; i)aF  Return the project file declaring the workspace for ``member_pyproject``.

Walks upwards from ``member_pyproject``'s directory for the first
project file that declares a workspace, checking ``pyproject.toml``
before ``nab.toml`` in each directory.  Returns that file's path, or
``None`` when nothing on the walk declares one.

The starting directory counts, so ``nab lock`` on a workspace root
activates discovery too.  Filesystem-level errors and TOML parse
errors during the walk are swallowed: a malformed sibling should not
prevent discovery from finding a valid root above it.
pyproject.tomlr!   N)resolveparentparentsr3   r#   r$   r%   r(   r&   r'   r9   )r@   	start_dirrD   	candidater)   r.   declared_labels   &       r   discover_workspace_rootrJ   j   s     !((*11I1y001 #33Vm5KLI^^D))Q ::a=D *  6dIFH#   M 2  *)/1F1FG s0   B4 B!	7B4!B1,B41B44 CCc               (    V ^8  d   QhRRRRRRRR/# )	r   r=   zIterable[str]root_dirr   declared_instrr   tuple[LocalSource, ...]r   )r   s   "r   r   r      s2     O OO)-O<?OOr   c          	       a . p/ pV  EF  o\         ;QJ d    V3R lR 4       F  '       g   K   RM	  RM! V3R lR 4       4      '       d   V RS: 2p\        V4      h\        VS4      pVf   V RS: R2p\        V4      hVR,          p\        V4      P                  '       g   V RS: R	V 2p\        V4      h\        V4      pVP                  R
/ 4      p	\        V	\        4      '       g&   V R\        V	4      P                   2p\        V4      hV	P                  R4      p
\        V
\        4      '       d	   V
'       g   V RV
: R2p\        V4      h\        V
4      pW9   d"   V RV: RWK,          : RS: 2p\        V4      hSWK&   VP                  \        V
\        V4      RR7      4       EK  	  \        V4      # )a  Synthesise a :class:`LocalSource` per declared workspace member.

``members`` are the declared paths, resolved against ``root_dir``;
``declared_in`` names the declaring file and prefixes the errors.
Each entry must be a literal path; any entry containing ``*``, ``?``
or ``[`` raises :class:`WorkspaceDiscoveryError` with a message
naming the offending entry.  For every member directory the function
opens ``<member>/pyproject.toml`` and requires ``[project].name``;
missing pyproject or missing name is a hard error.

Two members declaring the same canonical name raises
:class:`WorkspaceDiscoveryError`.  The returned tuple preserves
declaration order, which makes ``nab lock`` output stable when the
list of members is itself stable.

Members are marked ``editable``; a workspace member installs
editably by default, matching uv.  Explicit
``[[tool.nab.local-sources]]`` entries default to non-editable.
c              3  ,   <"   T F	  qS9   x  K  	  R # 5i)Nr   ).0chentrys   & r   	<genexpr>*workspace_local_sources.<locals>.<genexpr>   s     +UrU{Us   z*?[TFza: globs in workspace members are not supported in nab; list members literally.  Offending entry: z: workspace member z  is not a usable filesystem pathrB   z has no pyproject.toml at projectz2: workspace member [project] must be a table, got r2   z4: workspace member must declare [project].name (got )z5: workspace members declare duplicate canonical name z via entries z and )r2   patheditable)anyr   r	   r   should_readr,   r4   r5   r6   typer   rN   r   appendr
   tuple)r=   rL   rM   sourcesseenr+   
member_dirr@   member_dataproject_tabler2   	canonicalrT   s   &$$         @r   workspace_local_sourcesrf      s   , "$GD3+U+333+U+++-  &&+Y0 
 *#..!(E2
-25)23  *#..%(88*+777-25) <&&6%79  *#..'(89#	26-..#$ %#M2;;<>  *#..  ($$$D#$ %((,xq2  *#..%d+	-  ##,-}T_<Oy" 
 *#..{3z?TRSi l >r   c                    V ^8  d   QhRRRR/# )r   	root_filer   r   rO   r   )r   s   "r   r   r      s     ' 'd '/F 'r   c                J   \        V 4      p\        W4      w  r#\        V\        4      '       g)   V  RV R\	        V4      P
                   2p\        V4      hVP                  R4      p\        V\        4      '       g)   V  RV R\	        V4      P
                   2p\        V4      h. pV FV  p\        V\        4      '       g-   V  RV R\	        V4      P
                   RV: 2p\        V4      hVP                  V4       KX  	  \        VV P                  \        V 4      R7      # )aF  Synthesise :class:`LocalSource` entries from a workspace root.

Reads the members ``root_file`` declares, in the spelling its name
calls for, and resolves each member path against the root's
directory.  This is the path taken for a root the ancestor walk
found, which is read off disk rather than through the config
registry.
r"   z must be a table, got r=   z(.members must be a list of strings, got z&.members entries must be strings, got )rL   rM   )r,   r9   r5   r6   r]   r   r   r4   listrN   r^   rf   rD   )rh   	root_dataraw_workspacer8   r+   raw_membersr=   rT   s   &       r   read_workspace_membersrn      s8    "),I1)GMmT**kE7"8m9L9U9U8VW 	 &c**##I.Kk4((kE7 #!+.778: 	 &c**G%%%+Rw '!!%e!5!5 6b	C  *#..u  #!!	N r   c               $    V ^8  d   QhRRRRRR/# )r   explicitzIterable[LocalSource]
discoveredr   rO   r   )r   s   "r   r   r     s&      #% r   c                X   \        V 4      pV Uu0 uF  p\        VP                  4      kK  	  pp\        V4      pV F[  p\        VP                  4      V9   d.   \        P                  RVP                  VP                  4       KJ  VP                  V4       K]  	  \        V4      # u upi )a  Combine explicit and workspace-discovered local sources.

Explicit ``[[tool.nab.local-sources]]`` entries always win.  When a
discovered member shares a canonical name with an explicit entry,
the discovered entry is dropped and one INFO log line records the
shadow so the user can audit what was overridden.  The order is
explicit entries first, then unshadowed discovered entries in the
order they were declared.
zIworkspace member %r at %s shadowed by explicit [[tool.nab.local-sources]])r_   r   r2   rj   loggerinforY   r^   )rp   rq   explicit_tuplesexplicit_namesoutsrcs   &&     r   merge_workspace_local_sourcesrz     s     8_N9GHA'/NH
~
CSXX&.8KK.	 

3  : Is   B')r   r   rJ   rz   rn   rf   )%r   
__future__r   loggingdataclassesr   typingr   r   r$   _tomlr   _vendor.packaging.utilsr   pathsr   r	   providerr
   collections.abcr   pathlibr   __all__	getLoggerr   rs   
ValueErrorr   r3   r,   r9   r   rJ   rf   rn   rz   r   r   r   <module>r      s   & #  ! %  # 6 + !( 
		8	$Jj J 4$'$ $d#  $6Od'Tr   