
    Kian                    B   d Z ddlmZ ddlZddlZddl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mZ ddlmZ ddlmZ ddlmZ  eej,                  j/                  d	 e e ej4                         xs d
d                        Z ej8                  e      Zg dZd*d+dZ d*d,dZ!d-dZ"d-dZ#d.dZ$d/dZ%d0dZ&d1dZ' eej,                  j/                  dd            Z(ej,                  j/                  dd      jS                         dv Z*d2dZ+ e	jX                  d      Z-d3d4dZ.d/dZ/	 d*	 	 	 	 	 d5dZ0	 d*	 	 	 	 	 d5dZ1 eej,                  j/                  d d!            Z2d"Z3d6d#Z4e2f	 	 	 	 	 	 	 d7d$Z5	 	 	 	 d8d%Z6	 d*	 	 	 	 	 	 	 d9d&Z7	 	 d:	 	 	 	 	 	 	 	 	 d;d'Z8d(Z9d<d)Z:y)=zIncremental graph update logic.

Detects changed files via git diff, re-parses only changed + impacted files,
and updates the graph accordingly. Also supports CLI invocation for hooks.
    )annotationsN)PathPurePosixPath)Optional   )
GraphStore)
CodeParserCRG_PARSE_WORKERS      )z.code-review-graph/**znode_modules/**z.git/**z__pycache__/**z*.pycz.venv/**zvenv/**zdist/**zbuild/**z.next/**z	target/**z	vendor/**zbootstrap/cache/**zpublic/build/**z
.bundle/**z
.gradle/**z*.jarz.dart_tool/**z.pub-cache/**zcoverage/**z	.cache/**z*.min.jsz	*.min.cssz*.mapz*.lockzpackage-lock.jsonz	yarn.lockz*.dbz*.sqlitez*.db-journalz*.db-walc                    | xs t        j                         }||j                  k7  r1|dz  j                         r|S |j                  }||j                  k7  r1|dz  j                         r|S y)z6Walk up from start to find the nearest .git directory.z.gitN)r   cwdparentexists)startcurrents     i/home/jay/workspace/scripts/.codegraph-venv/lib/python3.12/site-packages/code_review_graph/incremental.pyfind_repo_rootr   J   sf    !txxzG
W^^
#f$$&N.. W^^
# 	&  "    c                "   t         j                  j                  dd      j                         }|r9t	        |      j                         j                         }|j                         r|S t        |       }|r|S | xs t	        j                         S )u  Find the project root.

    Resolution order (highest precedence first):

    1. ``CRG_REPO_ROOT`` environment variable — explicit override for
       anyone scripting the CLI from outside the repo (CI jobs, daemons,
       multi-repo orchestrators). See: #155
    2. Git repository root via :func:`find_repo_root` from ``start``.
    3. ``start`` itself (or cwd if no start given).
    CRG_REPO_ROOT )
osenvirongetstripr   
expanduserresolver   r   r   )r   env_overrideproots       r   find_project_rootr"   V   sq     ::>>/26<<>L))+33588:H% DDHHJr   c                X   t         j                  j                  dd      j                         }|r(t	        |      j                         j                         }n| dz  }|j                  dd       |dz  }|j                         s	 |j                  d       |S |S # t        $ r Y |S w xY w)u  Return the directory where this project's graph data lives.

    By default, ``<repo_root>/.code-review-graph``. If the
    ``CRG_DATA_DIR`` environment variable is set, it is used verbatim
    instead — letting you keep graphs outside the working tree (useful
    for ephemeral workspaces, Docker volumes, or shared caches). See: #155

    The directory is created if it does not already exist; an inner
    ``.gitignore`` (with ``*``) is written so any accidentally-nested
    files never get committed. Both are idempotent.
    CRG_DATA_DIRr   .code-review-graphT)parentsexist_ok
.gitignoreu   # Auto-generated by code-review-graph — do not commit database files.
# The graph.db contains absolute paths and code structure metadata.
*
)r   r   r   r   r   r   r   mkdirr   
write_textOSError)	repo_rootr   data_dirinner_gitignores       r   get_data_dirr/   l   s     ::>>."5;;=L%002::<33NN4$N/-O!!#	&& O8O	  	O		s   B 	B)(B)c                    t        |       }|dz  }| dz  }|j                         r!|j                         s|j                  |       dD ]+  }| d| z  }|j                         s|j                          - |S )zDetermine the database path for a repository.

    Respects ``CRG_DATA_DIR`` (see :func:`get_data_dir`). Migrates a
    legacy top-level ``.code-review-graph.db`` file into the new
    directory when it exists (WAL/SHM side-files are discarded).
    zgraph.dbz.code-review-graph.db)z-walz-shmz-journal)r/   r   renameunlink)r,   crg_dirnew_db	legacy_dbsuffixsides         r   get_db_pathr8      s     9%Gz!F 33I&--/ . 26(;;;;=KKM
 Mr   c                b   | dz  }|j                         r|j                  d      nd}|j                         D ]>  }|j                         }|r|j	                  d      r'|dk(  s|j	                  d      s> y d	}|r|j                  d
      sd
nd}|j                  ||z   |z   d       |ryy)a   Ensure repo-level .gitignore excludes ``.code-review-graph/``.

    Returns one of:
    - ``created``: .gitignore was created with the entry
    - ``updated``: entry was appended to existing .gitignore
    - ``already-present``: no changes were needed
    r(   zutf-8)encodingr   #r%   z.code-review-graph/zalready-presentz1# Added by code-review-graph
.code-review-graph/

updatedcreated)r   	read_text
splitlinesr   
startswithendswithr*   )r,   gitignore_pathexistingraw_linelineblockprefixs          r   "ensure_repo_gitignore_excludes_crgrI      s     -N=K=R=R=T~'''9Z\H'') %~~ts+''4??;P+Q$% BEh&7&7&=T2Fh/%7'Jr   c                    t        t              }| dz  }|j                         rY|j                         j	                         D ]8  }|j                         }|s|j                  d      r(|j                  |       : |S )z8Load ignore patterns from .code-review-graphignore file.z.code-review-graphignorer;   )listDEFAULT_IGNORE_PATTERNSr   r?   r@   r   rA   append)r,   patternsignore_filerF   s       r   _load_ignore_patternsrP      sq    +,H88K))+668 	&D::<DDOOC0%	& Or   c                     t         fd|D              ryt               j                  }|D ]&  }|j                  d      s|dd }d|v s|s!||v s& y y)a  Check if a path matches any ignore pattern.

    Handles nested occurrences of ``<dir>/**`` patterns: for example,
    ``node_modules/**`` also matches ``packages/app/node_modules/foo.js``
    inside monorepos. ``fnmatch`` alone treats ``*`` as not crossing ``/``
    and only matches the prefix, so we additionally test each path segment
    against the bare prefix of ``<dir>/**`` patterns. See: #91
    c              3  J   K   | ]  }t        j                   |        y wN)fnmatch).0r    paths     r   	<genexpr>z!_should_ignore.<locals>.<genexpr>   s     
67??4#
6s    #Tz/**N/F)anyr   partsrB   )rV   rN   r[   r    rH   s   `    r   _should_ignorer\      sn     
6X
66 $%%E 	zz% 3B &=U?	 r   c                \    	 | j                         dd }d|v S # t        t        f$ r Y yw xY w)z4Quick heuristic: check if file appears to be binary.Ni        T)
read_bytesr+   PermissionError)rV   chunks     r   
_is_binaryrb      s<    !%4(%_% s    ++CRG_GIT_TIMEOUT30CRG_RECURSE_SUBMODULESr   )1trueyesc                   d}d}	 t        j                  g dddt        |       t              }|j                  dk(  r|j
                  j                         }	 t        j                  g dddt        |       t              }|j                  dk(  r|j
                  j                         }||fS # t         j                  t        f$ r Y rw xY w# t         j                  t        f$ r Y ||fS w xY w)z:Return (branch_name, head_sha) for the current repo state.r   )git	rev-parsez--abbrev-refHEADTcapture_outputtextr   timeoutr   )rj   rk   rl   )	
subprocessrunstr_GIT_TIMEOUT
returncodestdoutr   TimeoutExpiredFileNotFoundError)r,   branchsharesults       r   _git_branch_infor|      s    F
C	8dI

 !]]((*F	(dI

 !--%%'C 3; %%'89  %%'89 3;s%   AB/ AC /C
CC-,C-z^[A-Za-z0-9_.~^/@{}\-]+$c                   t         j                  |      st        j                  d|       g S 	 t	        j
                  ddd|dgddt        |       t              }|j                  dk7  r)t	        j
                  g d	ddt        |       t              }|j                  j                         D cg c]#  }|j                         s|j                         % }}|S c c}w # t        t        j                  f$ r g cY S w xY w)
z'Get list of changed files via git diff.zInvalid git ref rejected: %srj   diff--name-onlyz--Trm   r   )rj   r~   r   z--cached)_SAFE_GIT_REFmatchloggerwarningrq   rr   rs   rt   ru   rv   r@   r   rx   rw   )r,   baser{   ffiless        r   get_changed_filesr     s    t$5t<	FM46I 
 !^^:#	N$F %+MM$<$<$>Lq!'')LL Mz889 	s*   B C  /CCC  C   C>=C>c                v   	 t        j                  g dddt        |       t              }g }|j                  j                         D ]N  }t        |      dkD  s|dd j                         }d|v r|j                  dd      d   }|j                  |       P |S # t        t         j                  f$ r g cY S w xY w)z7Get all modified files (staged + unstaged + untracked).)rj   statusz--porcelainTrm      Nz -> r   )rq   rr   rs   rt   rv   r@   lenr   splitrM   rx   rw   )r,   r{   r   rF   entrys        r   get_staged_and_unstagedr   8  s    ,I 
 MM,,. 	$D4y1}QR(U?!KK215EU#	$ z889 	s   AB A B B87B8c                j   |t         }ddg}|r|j                  d       	 t        j                  |ddt	        |       t
              }|j                  j                         D cg c]#  }|j                         s|j                         % c}S c c}w # t        t        j                  f$ r g cY S w xY w)ac  Get all files tracked by git.

    Args:
        repo_root: Repository root directory.
        recurse_submodules: If True, pass ``--recurse-submodules`` to
            ``git ls-files`` so that files inside git submodules are
            included.  When *None* (default), falls back to the
            ``CRG_RECURSE_SUBMODULES`` environment variable.
    rj   zls-filesz--recurse-submodulesTrm   )_RECURSE_SUBMODULESrM   rq   rr   rs   rt   rv   r@   r   rx   rw   )r,   recurse_submodulescmdr{   r   s        r   get_all_tracked_filesr   O  s     !0*
C

)*
I 
 $*==#;#;#=Ka	KKKz889 	s*   AB $B:BB B B21B2c                   t        |       }t               }g }t        | |      }|r|}nF| j                  d      D cg c],  }|j	                         rt        |j                  |             . }}|D ]e  }t        ||      r| |z  }	|	j	                         s&|	j                         r7|j                  |	      It        |	      rU|j                  |       g |S c c}w )a  Collect all parseable files in the repo, respecting ignore patterns.

    Args:
        repo_root: Repository root directory.
        recurse_submodules: If True, include files from git submodules.
            When *None*, falls back to ``CRG_RECURSE_SUBMODULES`` env var.
    *)rP   r	   r   rglobis_filers   relative_tor\   
is_symlinkdetect_languagerb   rM   )
r,   r   ignore_patternsparserr   tracked
candidatesr    rel_path	full_paths
             r   collect_all_filesr   p  s     ,I6O\FE $I/ABG

 __S)
yy{ i()

 
  (O4(	  "!!!),4i X L)
s   1CCRG_DEPENDENT_HOPS2i  c                   t               }| j                  |      }|D ]-  }|j                  dk(  s|j                  |j                         / | j                  |      }|D ]L  }| j                  |j                        D ],  }|j                  dv s|j                  |j                         . N |j                  |       |S )z<Find files that directly depend on *file_path* (single hop).IMPORTS_FROM)CALLSr   INHERITS
IMPLEMENTS)setget_edges_by_targetkindadd	file_pathget_nodes_by_filequalified_namediscard)storer   
dependentsedgesenodesnodes          r   _single_hop_dependentsr     s    5J%%i0E (66^#NN1;;'( ##I.E ,**4+>+>? 	,AvvLLq{{+	,,
 y!r   c                   t               }|h}|h}t        |      D ]  }t               }|D ]5  }t        | |      }	|	|z
  }
|j                  |
       |j                  |
       7 |j                  |       |}|s t        |      S t	        |      t
        kD  s{t        j                  dt	        |      |       t        |      dt
         c S  t        |      S )zFind files that import from or depend on the given file.

    Performs up to *max_hops* iterations of expansion (default 2).
    Stops early if the total exceeds 500 files.
    z-Dependent expansion capped at %d files for %sN)	r   ranger   updater   _MAX_DEPENDENT_FILESr   r   rK   )r   r   max_hopsall_dependentsvisitedfrontier_hopnext_frontierfpdepsnew_depss              r   find_dependentsr     s      #uN"G#Hh ?"%% 	+B)%4Dg~H!!(+  *		+
 	}%   ~!55NN?N#Y
 '(=)=>>#?$ r   c                (   | \  }}t        |      |z  }	 |j                         }t        j                  |      j	                         }t               }|j                  ||      \  }}|||d|fS # t        $ r}	|g g t        |	      dfcY d}	~	S d}	~	ww xY w)zParse one file in a worker process.

    Returns ``(rel_path, nodes, edges, error_or_none, file_hash)``.
    Must be a module-level function so ``ProcessPoolExecutor`` can
    serialise it across processes.
    Nr   )	r   r_   hashlibsha256	hexdigestr	   parse_bytes	Exceptionrs   )
argsr   repo_root_strabs_pathrawfhashr   r   r   r   s
             r   _parse_single_filer     s     #HmM"X-H.!!#s#--/))(C8u%e44 ."b#a&"--.s   AA. .	B7BBBc                H   t               }t        | |      }t        |j                               }|D ch c]  }t	        | |z         }}||z
  }|D ]  }	|j                  |	        |r|j                          d}
d}g }t        |      }t        j                  j                  dd      dk(  }|s|dk  rt        |d      D ]  \  }}| |z  }	 |j                         }t        j                  |      j                         }|j!                  ||      \  }}|j#                  t	        |      |||       |
t        |      z  }
|t        |      z  }|d
z  dk(  s||k(  st,        j1                  d||        n	|D cg c]  }|t	        |       f }}t2        j4                  j7                  t8              5 }t        |j;                  t<        |d      d      D ]  \  }\  }}}}}|r,t,        j/                  d	||       |j)                  ||d       :| |z  }|j#                  t	        |      |||       |
t        |      z  }
|t        |      z  }|dz  dk(  s||k(  st,        j1                  d||        	 ddd       |j?                  dtA        jB                  d             |j?                  dd       tE        |       \  }}|r|j?                  d|       |r|j?                  d|       |j                          t        |      |
||dS c c}w # t$        t&        f$ r(}|j)                  |t	        |      d       Y d}~d}~wt*        $ r?}t,        j/                  d	||       |j)                  |t	        |      d       Y d}~<d}~ww xY wc c}w # 1 sw Y   !xY w)a  Full rebuild of the entire graph.

    Args:
        repo_root: Repository root directory.
        store: Graph database store.
        recurse_submodules: If True, include files from git submodules.
            When *None*, falls back to ``CRG_RECURSE_SUBMODULES`` env var.
    r   CRG_SERIAL_PARSEr   rf   r   r   fileerrorNError parsing %s: %s2   zProgress: %d/%d files parsedmax_workers   	chunksize   last_updated%Y-%m-%dT%H:%M:%Slast_build_typefull
git_branchgit_head_sha)files_parsedtotal_nodestotal_edgeserrors)#r	   r   r   get_all_filesrs   remove_file_datacommitr   r   r   r   	enumerater_   r   r   r   r   store_file_nodes_edgesr+   r`   rM   r   r   r   info
concurrentfuturesProcessPoolExecutor_MAX_PARSE_WORKERSmapr   set_metadatatimestrftimer|   )r,   r   r   r   r   existing_filesr   current_absstale_filesstaler   r   r   
file_count
use_serialir   r   sourcer   r   r   r   	args_listexecutorr   ry   rz   s                               r   
full_buildr    s    \Fi);<E ,,./N/45!3y1}%5K5 ;.K &u%& KKFUJ 2B73>JZ!^$UA. 	KKAx!H,IC"--/v.88:%11)VDu,,S^UE5Qs5z)s5z) 2v{a:o:AzJ	K$ AFFHhI/F	F33* 4 
 	O=F/bI1> O99HeUE5 NN#98UKMM8e"DE%0	,,	NE5% s5z)s5z)s7a<1
?KK >:NO	O& 
~t}}5H'IJ	(&1"9-KFC<0
>3/	LLN E
""	 C 66 _- Cx#a&ABB C5xCx#a&ABBC G	O 	OsC   LBL;N5B(NNN!MN4N

NN!c           	     	   t               }t        |       }|t        | |      }|sdddg g dS t               }|D ]V  }t	        | |z        }t        ||      }	|	D ]5  }
	 |j                  t	        t        |
      j                  |                    7 X t        |      |z  }d}d}g }g }d}|D ]  }t        ||      r| |z  }|j                         s|j                  t	        |             d}B|j                  |      T	 |j                         }t        j                   |      j#                         }|j%                  t	        |            }|r|d   j&                  |k(  r|j-                  |        |r|j/                          t0        j2                  j5                  dd      dk(  }|st7        |      d	k  r|D ]  }| |z  }	 |j                         }t        j                   |      j#                         }|j9                  ||      \  }}|j;                  t	        |      |||       |t7        |      z  }|t7        |      z  } n|D cg c]  }|t	        |       f }}tB        jD                  jG                  tH              5 }|jK                  tL        |d      D ]r  \  }}}}}|r,t>        jA                  d||       |j-                  ||d
       7|j;                  t	        | |z        |||       |t7        |      z  }|t7        |      z  }t 	 ddd       |jO                  dtQ        jR                  d             |jO                  dd       tU        |       \  }}|r|jO                  d|       |r|jO                  d|       |j/                          t7        |      ||tW        |      tW        |      |dS # t        $ r |j                  |
       Y w xY w# t(        t*        f$ r Y w xY w# t(        t*        f$ r(}|j-                  |t	        |      d
       Y d}~ld}~wt<        $ r?}t>        jA                  d||       |j-                  |t	        |      d
       Y d}~d}~ww xY wc c}w # 1 sw Y   gxY w)z<Incremental update: re-parse changed + dependent files only.Nr   )files_updatedr   r   changed_filesdependent_filesFTr   r   rf   r   r   r   r   r   r   r   r   r   incrementalr   r   )r  r   r   r  r  r   ),r	   rP   r   r   rs   r   r   r   r   
ValueErrorr\   r   r   r   r_   r   r   r   r   	file_hashr+   r`   rM   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r|   rK   )r,   r   r   r  r   r   r  r   r   r   d	all_filesr   r   r   to_parseremoved_anyr   r   r   existing_nodesr   r   r   r   r   r   r   r   ry   rz   s                                  r   incremental_updater  D  s    \F+I6O ))T:!
 	
 !$O! '	H,-	ui0 	'A'##CQ(;(;I(F$GH	'' M"_4IKKF HK "(O4x'!""3x=1K!!(+3	%%'CNN3'113E"44S]CN."3"="="F 	!'".  2B73>JS]Q&  	CH 8+HC!,,.v.88:%11(FCu,,S]E5%Ps5z)s5z)	C AIIHhI/I	I33* 4 
 	*8@"I 9E 9 *4%u NN#98UKMM8e"DE,,	H,-ueU s5z)s5z)*	*  
~t}}5H'IJ	(-8"9-KFC<0
>3/	LLN Y""m,0 g  '##A&'< ) 		* _- Cx#a&ABB C5xCx#a&ABBC J	* 	*s]   2OA!O"BO8Q8	BQ=OO"O54O58Q5P**Q564Q00Q5=Rg333333?c                   	
 ddl 
ddlm} ddlm} t               	t                G 	 
fdd|      } |       } |       }|j                  |t               d       |j                          t        j                  d	        	 ddl}	 |j                  d
       # t        $ r |j                          Y nw xY w|j!                          t        j                  d       y)zWatch for file changes and auto-update the graph.

    Uses a 300ms debounce to batch rapid-fire saves into a single update.
    r   N)FileSystemEventHandler)Observerc                  d    e Zd ZfdZd
 fdZd Zd Z fdZdfdZd Z	dfdZ
y	)!watch.<locals>.GraphUpdateHandlerc                \    t               | _        j                         | _        d | _        y rS   )r   _pendingLock_lock_timer)self	threadings    r   __init__z*watch.<locals>.GraphUpdateHandler.__init__  s!    &)eDM")DJ26DKr   c                    t        |      j                         ry	 t        t        |      j                              }t        |      ryj                  t        |            yy# t        $ r Y yw xY w)NFT)r   r   rs   r   r  r\   r   )r  rV   relr   r   r,   s      r   _should_handlez0watch.<locals>.GraphUpdateHandler._should_handle  sr    Dz$$&$t*00;< c?3%%d4j19  s   #A) )	A54A5c                    |j                   ry | j                  |j                        r| j                  |j                         y y rS   is_directoryr  src_path	_scheduler  events     r   on_modifiedz-watch.<locals>.GraphUpdateHandler.on_modified  5    !!""5>>2u~~. 3r   c                    |j                   ry | j                  |j                        r| j                  |j                         y y rS   r   r$  s     r   
on_createdz,watch.<locals>.GraphUpdateHandler.on_created  r'  r   c                   |j                   ry 	 t        t        |j                        j	                              }t        |      ry 	 j                  |j                         j                          t        j                  d|       y # t
        $ r Y y w xY w# t        $ r!}t        j                  d||       Y d }~y d }~ww xY w)NzRemoved: %szError removing %s: %s)r!  rs   r   r"  r   r  r\   r   r   r   r   r   r   )r  r%  r  r   r   r,   r   s       r   
on_deletedz,watch.<locals>.GraphUpdateHandler.on_deleted  s    !!$u~~.::9EF c?3>&&u~~6M3/    >4c1==>s*   -B AB 	BB	C%CCc                H   | j                   5  | j                  j                  |       | j                  | j                  j	                          j                  t        | j                        | _        | j                  j                          ddd       y# 1 sw Y   yxY w)z5Add file to pending set and reset the debounce timer.N)	r  r  r   r  cancelTimer_DEBOUNCE_SECONDS_flushr   )r  r   r  s     r   r#  z+watch.<locals>.GraphUpdateHandler._schedule  sv     $!!(+;;*KK&&('oo%t{{ !!#$ $ $s   BBB!c                    | j                   5  t        | j                        }| j                  j                          d| _        ddd       D ]  }| j                  |        y# 1 sw Y   "xY w)z4Process all pending files after the debounce window.N)r  rK   r  clearr  _update_file)r  pathsr   s      r   r0  z(watch.<locals>.GraphUpdateHandler._flush  sb     #T]]+##%"#
 " ,!!(+,# #s   7A%%A.c                ~   t        |      }|j                         sy |j                         ry t        |      ry 	 |j	                         }t        j                  |      j                         }	j                  ||      \  }}j                  ||||       j                  dt        j                  d             j                          t        |j                  
            }t         j#                  d|t%        |      t%        |             y # t&        $ r!}t         j)                  d||       Y d }~y d }~ww xY w)Nr   r   z Updated: %s (%d nodes, %d edges)zError updating %s: %s)r   r   r   rb   r_   r   r   r   r   r   r   r   r   r   rs   r   r   r   r   r   r   )r  r   rV   r   r   r   r   r  r   r   r,   r   s            r   r3  z.watch.<locals>.GraphUpdateHandler._update_file  s   >D<<> $C*v.88:%11$?u,,XueUK"""DMM2E$F $**9566USZ  C4hBBCs   CD 	D<D77D<N)rV   rs   returnbool)r   rs   )__name__
__module____qualname__r  r  r&  r)  r+  r#  r0  r3  )r   r   r,   r   r  s   r   GraphUpdateHandlerr    s2    	7
		/	/	>"		$	,	C 	Cr   r;  T)	recursivez+Watching %s for changes... (Ctrl+C to stop)r   zWatch stopped.)r  watchdog.eventsr  watchdog.observersr  r	   rP   schedulers   r   r   r   r   sleepKeyboardInterruptstopjoin)r,   r   r  r  r;  handlerobserver_timer   r   r  s   ``      @@@r   watchrG    s    
 6+\F+I6O\C \C3 \C| !"GzHgs9~>NN
KK=yIKKN  MMO
KK !s   B% %C CrS   )r   Path | Noner6  zOptional[Path])r   rH  r6  r   )r,   r   r6  r   )r,   r   r6  rs   )r,   r   r6  	list[str])rV   rs   rN   rI  r6  r7  )rV   r   r6  r7  )r,   r   r6  tuple[str, str])HEAD~1)r,   r   r   rs   r6  rI  )r,   r   r   bool | Noner6  rI  )r   r   r   rs   r6  zset[str])r   r   r   rs   r   intr6  rI  )r   rJ  r6  z'tuple[str, list, list, str | None, str])r,   r   r   r   r   rL  r6  dict)rK  N)
r,   r   r   r   r   rs   r  zlist[str] | Noner6  rN  )r,   r   r   r   r6  None);__doc__
__future__r   concurrent.futuresr   rT   r   loggingr   rerq   r   pathlibr   r   typingr   graphr   r   r	   rM  r   r   rs   min	cpu_countr   	getLoggerr8  r   rL   r   r"   r/   r8   rI   rP   r\   rb   rt   lowerr   r|   compiler   r   r   r   r   _MAX_DEPENDENT_HOPSr   r   r   r   r  r  r/  rG   r   r   <module>r_     sR   #     	 	   '   S!41a89   
		8	$% P	, F46	8 2::>>"3T:;
 jjnnb%'!" 
4 

6782 '+# F '+))#) )X "**..)=sCD  * (      	 D.
.,.0 '+XXX $X 
	X| &*	zzz z $	z
 
zD  x"r   