+
    2jp                    @   R t ^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	t	^ RI
Ht ^ RIHtHt ^ RIHt ^ RIHtHt ^RIHt ^RIHt ^R	IHt ]'       d   ^ R
IHt ]P:                  ! ]4      t. R5Ot Rt!Rt"Rt#Rt$Rt%R6t&Rt'Rt(]'](3t)]*! RR.4      t+ ! R R],4      t-]! RRR7       ! R R4      4       t.R R lt/R R lt0R R lt1R  R! lt2R" R# lt3R$ R% lt4R& R' lt5 ! R( R4      t6R) R* lt7R+ R, lt8R- R. lt9R/ R0 lt:R1 R2 lt; ! R3 R]4      t< ! R4 R4      t=R# )7aO  On-disk cache for nab-index.

Stores PEP 691 Simple API responses (JSON body plus a sidecar cache
policy file) and PEP 658 wheel metadata (raw text, treated as
immutable). The cache is consulted by :class:`CachedAsyncSimpleClient`
before any HTTP transport call.

Layout under ``root``:

    simple-v1/<index>[-<serialization>]/<package>.json    <- PEP 691 JSON body
    simple-v1/<index>[-<serialization>]/<package>.policy  <- {fetched_at, max_age,
                                                              etag, page_url,
                                                              body_digest}
    simple-parsed-v0/<index>[-<serialization>]/<package>.parsed  <- parsed blob
    simple-neg-v0/<index>[-<serialization>]/<package>.neg <- {fetched_at, max_age, etag}
    metadata-v1/<index>/<package>/<url digest>.metadata
    sdist-v1/<index>/<package>/<version>.json  <- {pkg_info, pyproject}

An index pinned to one serialization gets its own listings directory,
since a stored body records nothing about which serialization it came from.

A versioned bucket name (``simple-v1``) gives zero-cost schema
migration: when the on-disk format changes, bump the suffix and the
old directory is harmless.

When the index serves PEP 503 HTML the stored body is nab's own rendering
of the page. A 304 revalidation keeps the old body, so changing that
rendering also needs the suffix bumped to reach warm caches.

A resolve writes two more buckets under the same root, holding upstream
source trees:

    vcs/vcs/<repo key>/<commit sha>/   <- shallow clone
    archive/<archive digest>/          <- extracted archive
)annotationsN)suppress)	dataclassreplace)Path)TYPE_CHECKINGProtocol)atomic_write)corruption_reason)SimpleSerialization)IteratorCacheBackendCachePolicy	NullCacheOfflineErrorOnDiskCachev1v0vcsarchivezhttps://pypi.org/simplezhttp://pypi.org/simplec                      ] tR t^_tRtRtR# )r   zARaised when offline mode is set and a needed entry is not cached. N)__name__
__module____qualname____firstlineno____doc____static_attributes__r       Z/home/user/billing-ledger-validation/.venv/lib/python3.14/site-packages/nab_index/cache.pyr   r   _   s    Kr   T)frozenslotsc                  f    ] tR t^ct$ RtR]R&   R]R&   R]R&   RtR]R&   RtR]R	&   RR
 R lltRt	R# )r   aC  RFC 9111-style freshness policy for one Simple API entry.

``fetched_at`` is the start of the freshness window: when nab received the
response, less any Age a relaying shared cache reported.

``page_url`` is the URL the stored body was retrieved from, the base its
relative entries resolve against. It is ``None`` for the negative
sentinel, which has no body, and for an entry cached without it.

``body_digest`` is the sha256 hex of the raw body this policy governs, and
binds a parsed-listing blob to that body. It is ``None`` for an older
policy or a bodyless negative entry.
int
fetched_atmax_age
str | NoneetagNpage_urlbody_digestc                    V ^8  d   QhRRRR/# )   now
int | Nonereturnboolr   )formats   "r   __annotate__CachePolicy.__annotate__y   s     8 8J 8$ 8r   c                    Vf   \        \        P                  ! 4       4      MTpW P                  ,
          V P                  8  # )z>Return True if the entry is still within its freshness window.)r#   timer$   r%   )selfr,   currents   && r   is_freshCachePolicy.is_freshy   s.    &)k#diik"s(4<<77r   r   N)
r   r   r   r   r   __annotations__r(   r)   r7   r   r   r   r   r   r   c   s7     OL
Hj"K"8 8r   c                    V ^8  d   QhRRRR/# r+   namestrr.   r/   r   )r0   s   "r   r1   r1      s     L L3 L4 Lr   c                   a  \         ;QJ d#    V 3R l\         4       F  '       g   K   R# 	  R# ! V 3R l\         4       4      # )z>Whether ``name`` is a bucket of records nab writes and parses.c              3  F   <"   T F  pSP                  V4      x  K  	  R # 5ir9   )
startswith).0prefixr=   s   & r   	<genexpr>#_is_entry_bucket.<locals>.<genexpr>   s     K5J6tv&&5Js   !TF)anyENTRY_BUCKET_PREFIXESr=   s   fr   _is_entry_bucketrI      s.    3K5JK33K3K3K5JKKKr   c                    V ^8  d   QhRRRR/# r<   r   )r0   s   "r   r1   r1      s     < <s <t <r   c                :    \        V 4      ;'       g
    V \        9   # )zCWhether ``name`` is a bucket directory nab owns under a cache root.)rI   SOURCE_BUCKETSrH   s   &r   is_recognized_bucketrM      s    D!;;T^%;;r   c                    V ^8  d   QhRRRR/# )r+   	index_urlr>   r.   r   )r0   s   "r   r1   r1      s     F Fc Fc Fr   c                    V P                  R4      \        9   d   R# \        P                  ! V P	                  R4      4      P                  4       R,          # )zAReturn a stable, filesystem-safe directory name for an index URL./pypiutf-8:N   N)rstripDEFAULT_PYPI_URLShashlibsha256encode	hexdigest)rO   s   &r   _index_dirnamer[      sA     11>>)**734>>@EEr   c               $    V ^8  d   QhRRRRRR/# )r+   pathr   databytesr.   Noner   )r0   s   "r   r1   r1      s!       E d r   c                V    V P                   P                  RRR7       \        W4       R# )zBCreate the cache bucket for ``path``, then write ``data`` into it.T)parentsexist_okN)parentmkdirr	   )r]   r^   s   &&r   _atomic_writerf      s!    KKdT2r   c                    V ^8  d   QhRRRR/# )r+   	componentr>   r.   r   )r0   s   "r   r1   r1      s      s s r   c                j    V R9   g   V \        V 4      P                  8w  d   RV : 2p\        V4      hV # )a|  Return ``component`` if it names exactly one path segment.

Each cache key component becomes one file or directory name under
the cache root, so it must be a single path segment. A value with
an embedded separator expands to a nested path, and ``.`` or ``..``
names a parent, so either would read or write a different file than
the key describes and return the wrong cache entry.
z2cache key component is not a single path segment: ) .z..)r   r=   
ValueError)rh   msgs   & r   _require_single_segmentrn      s9     O#yDO4H4H'HB9-Por   c               $    V ^8  d   QhRRRRRR/# )r+   r]   r   bitsr#   r.   r`   r   )r0   s   "r   r1   r1      s!     + +$ +c +d +r   c                    V P                  4       '       d   R# V P                  V P                  4       P                  V,          4       R# )z=Add ``bits`` to ``path``'s mode, leaving a symlink untouched.N)
is_symlinkchmodstatst_mode)r]   rp   s   &&r   _add_owner_moderv      s/    JJtyy{""T)*r   c                    V ^8  d   QhRRRR/# )r+   rootr   r.   r`   r   )r0   s   "r   r1   r1      s     ? ?$ ?4 ?r   c                *   \        V \        P                  4       \        P                  ! V 4       F^  w  rpV F&  p\        \        W4      \        P                  4       K(  	  V F&  p\        \        W4      \        P                  4       K(  	  K`  	  R# )a  Give the owner write on ``root`` and everything under it.

A clone carries read-only packfiles and an extracted archive keeps
the mode bits the archive declared, so either can leave a tree
``rmtree`` cannot take apart. Symlinks are skipped so no chmod lands
outside the root.
N)rv   rt   S_IRWXUoswalkr   S_IWUSR)rx   dirpathdirnames	filenamesr=   s   &    r   _make_removabler      sc     D$,,'(*$9DD/> DD/>  )6r   c                     ] tR t^tRtR]P                  /R R lltR R ltR R lt	R	 R
 lt
R R ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R  ltR! R" ltR# R$ ltR% R& ltR' R( ltR) R* ltR+ R, ltR- R. ltR/ R0 ltR1 R2 ltR3 R4 ltR5 R6 lt R7 R8 lt!R9 R: lt"R; R< lt#R=t$R># )?r   zFile-per-key cache for Simple API and wheel metadata.

Stores are best-effort: a write the filesystem refuses is dropped,
just as an entry that cannot be read is a miss.
serializationc               (    V ^8  d   QhRRRRRRRR/# )	r+   rx   r   rO   r>   r   r   r.   r`   r   )r0   s   "r   r1   OnDiskCache.__annotate__   s2     # ## #
 +# 
#r   c                  Wn         \        V4      V n        V\        P                  J d   V P                  MV P                   RVP
                   2pVR\         2,          V,          V n        VR\         2,          V,          V n	        VR\         2,          V,          V n        VR\         2,          V P                  ,          V n        VR\         2,          V P                  ,          V n        RV n        R# )	z4Create a cache rooted at ``root`` for ``index_url``.-simple-zsimple-parsed-zsimple-neg-	metadata-sdist-FN)_rootr[   _indexr   	NEGOTIATEvalueCACHE_VERSION_SIMPLE_simple_dirCACHE_VERSION_SIMPLE_PARSED_parsed_dirCACHE_VERSION_SIMPLE_NEG_neg_dirCACHE_VERSION_METADATA_metadata_dirCACHE_VERSION_SDIST
_sdist_dir_store_failed)r5   rx   rO   r   simple_indexs   &&&$ r   __init__OnDiskCache.__init__   s     
$Y/  3 = == KKKK=-"5"5!67 	
  G,@+A"BB\Q^$?#@AALP 	 -E,FGG,V!i0F/G$HH4;;V6*=)>!??$++M"r   c               $    V ^8  d   QhRRRRRR/# )r+   r]   r   r^   r_   r.   r/   r   )r0   s   "r   r1   r      s!      4 u  r   c                     \        W4       R#   \         dF   pT P                  '       g)   RT n        \        P	                  RT P
                  T4        Rp?R# Rp?ii ; i)zWrite ``data`` to ``path``, returning False if the write failed.

Only the first failure warns: a root that refuses one write
refuses the rest.
Tz'cannot store cache entries under %s: %sNF)rf   OSErrorr   loggerwarningr   )r5   r]   r^   excs   &&& r   _storeOnDiskCache._store   sV    	$%   	%%%%)"=tzz3 	s    A:AAc                    V ^8  d   QhRRRR/# )r+   packager>   r.   ztuple[Path, Path]r   )r0   s   "r   r1   r      s      S -> r   c                	v    \        V4      pV P                  V R 2,          pV P                  V R2,          pW43# ).json.policy)rn   r   )r5   r   segmentbodypolicys   &&   r   _simple_pathsOnDiskCache._simple_paths   sB    )'2WIU"33!!wiw$77~r   c                    V ^8  d   QhRRRR/# r+   r   r>   r.   r   r   )r0   s   "r   r1   r      s     6 6C 6D 6r   c                	D    \        V4      pV P                  V R 2,          # ).parsed)rn   r   r5   r   r   s   && r   _parsed_pathOnDiskCache._parsed_path   s$    )'2WIW"555r   c                    V ^8  d   QhRRRR/# r   r   )r0   s   "r   r1   r      s     0 0 0 0r   c                	D    \        V4      pV P                  V R 2,          # ).neg)rn   r   r   s   && r   	_neg_pathOnDiskCache._neg_path   s"    )'2}}'$///r   c               $    V ^8  d   QhRRRRRR/# )r+   r   r>   versionr.   r   r   )r0   s   "r   r1   r      s&     M M3 M M Mr   c                	h    \        V4      p\        V4      pV P                  V,          V R 2,          # )r   )rn   r   )r5   r   r   package_segmentversion_segments   &&&  r   _sdist_pathOnDiskCache._sdist_path   s2    1':1':0o5Fe3LLLr   c               $    V ^8  d   QhRRRRRR/# )r+   r   r>   metadata_urlr.   r   r   )r0   s   "r   r1   r      s&     	K 	Kc 	K 	K 	Kr   c                    \        V4      p\        P                  ! VP                  R4      4      P	                  4       pV P
                  V,          V R2,          # )zReturn the file holding the sidecar published at ``metadata_url``.

:pep:`658` attaches a sidecar to one file, so the wheels of a version
each have their own. The URL is digested to keep the key a single
path segment whatever path shape the index serves.
rS   	.metadata)rn   rW   rX   rY   rZ   r   )r5   r   r   r   digests   &&&  r   _metadata_pathOnDiskCache._metadata_path   sM     2': 3 3G <=GGI!!O3	6JJJr   c                    V ^8  d   QhRRRR/# r+   r   r>   r.   z tuple[bytes, CachePolicy] | Noner   )r0   s   "r   r1   r     s      # *J r   c                    V P                  V4      w  r# VP                  4       pVP                  4       p\        T4      pTf   \        P                  RT4       R# YV3#   \         d     R# i ; i)9Return ``(body_bytes, policy)`` if cached, else ``None``.N:Corrupt cache policy %s: not decodable; treating as a missr   
read_bytesr   _decode_policyr   r   )r5   r   	body_pathpolicy_pathpolicy_bytesr   r   s   &&     r   
get_simpleOnDiskCache.get_simple  sz    !%!3!3G!<		&113L'')D  ->NNL ~  		s    A A.-A.c               (    V ^8  d   QhRRRRRRRR/# 	r+   r   r>   r   r_   r   r   r.   r&   r   )r0   s   "r   r1   r     s(      # U K J r   c           
         V P                  V4      w  rEV P                  WB4      '       g   R# \        P                  ! V4      P	                  4       pV P                  V\        \        W6R7      4      4      '       g   R# V# )a  Write the body and the policy sidecar; return the body's digest.

The sidecar goes out only once the body has landed, so a dropped
body write cannot stamp an older body with the new one's ETag and
freshness window.  The sha256 of ``body`` is stamped into the stored
policy, overriding any value the caller passed, and returned as the
binding key for the parsed blob the caller writes next.  A write that
did not land returns ``None``, so no parsed blob is ever bound to a
body the store does not hold.
N)r)   )r   r   rW   rX   rZ   _encode_policyr   )r5   r   r   r   r   r   r   s   &&&&   r   
put_simpleOnDiskCache.put_simple  sj     "&!3!3G!<	{{9++%//1{{(KL
 
 r   c               $    V ^8  d   QhRRRRRR/# r+   r   r>   r   r   r.   r`   r   )r0   s   "r   r1   r   1  s!     9 9S 9+ 9$ 9r   c                b    V P                  V4      w  r4V P                  V\        V4      4       R# )zReplace the policy sidecar without touching the body.

Called after a 304 Not Modified, where the cached body is still
valid but the freshness window has slid forward.
N)r   r   r   )r5   r   r   _r   s   &&&  r   refresh_simple_policy!OnDiskCache.refresh_simple_policy1  s)     ++G4K!78r   c                    V ^8  d   QhRRRR/# r+   r   r>   r.   CachePolicy | Noner   )r0   s   "r   r1   r   :  s       1C r   c                    V P                  V4      w  r# VP                  4       p\        T4      pTf   \        P                  RT4       T#   \         d     R# i ; i)zReturn the freshness policy for a cached Simple entry, without its body.

The parsed-cache read path needs only the policy on a hit, so this reads
the small sidecar without the body. A corrupt policy logs and misses,
matching :meth:`get_simple`.
Nr   r   )r5   r   r   r   r   r   s   &&    r   get_simple_policyOnDiskCache.get_simple_policy:  sg     ++G4	&113L  ->NNL   		s   A AAc                    V ^8  d   QhRRRR/# r+   r   r>   r.   zbytes | Noner   )r0   s   "r   r1   r   N  s     	 	 	 	r   c                f     V P                  V4      P                  4       #   \         d     R# i ; i)zReturn the opaque parsed-listing blob for ``package``, or ``None``.

The blob is bytes to this layer; the record codec lives in
``parsed_listing``. An absent blob is a silent miss.
N)r   r   r   r5   r   s   &&r   get_simple_parsedOnDiskCache.get_simple_parsedN  s3    	$$W-88:: 		s   ! 00c                    V ^8  d   QhRRRR/# r+   r   r>   r.   r-   r   )r0   s   "r   r1   r   Y  s     
 
c 
j 
r   c                z     V P                  V4      P                  4       P                  #   \         d     R# i ; i)zReturn the on-disk size of the parsed-listing blob in bytes, or ``None``.

A single ``stat`` on the same path :meth:`get_simple_parsed` reads, so a
caller can size the blob before deciding whether to read and decode it.
An absent blob is a silent miss.
N)r   rt   st_sizer   r   s   &&r   get_simple_parsed_size"OnDiskCache.get_simple_parsed_sizeY  s9    	$$W-224<<< 		s   (+ ::c               $    V ^8  d   QhRRRRRR/# r+   r   r>   blobr_   r.   r`   r   )r0   s   "r   r1   r   e  s!     8 8 8E 8d 8r   c                <    \        V P                  V4      V4       R# )z@Write the opaque parsed-listing blob for ``package`` atomically.N)rf   r   r5   r   r   s   &&&r   put_simple_parsedOnDiskCache.put_simple_parsede  s    d''0$7r   c               $    V ^8  d   QhRRRRRR/# r+   r   r>   r   r.   r&   r   )r0   s   "r   r1   r   i  s!      C s z r   c                    V P                  W4      p VP                  4       p TP                  R4      #   \         d     R# i ; i  \         d    \
        P                  RT4        R# i ; i)zReturn the cached sidecar text for ``metadata_url``, or ``None``.

A present file that is not valid UTF-8 is a corrupt entry: logged
and treated as a miss. An absent file is a silent miss.
NrS   z?Corrupt cached metadata %s: not valid UTF-8; treating as a miss)r   r   r   decodeUnicodeDecodeErrorr   r   )r5   r   r   r]   raws   &&&  r   get_metadataOnDiskCache.get_metadatai  st     ""79	//#C	::g&&  		 " 	NNQ 	s    5 A AA!A,+A,c               (    V ^8  d   QhRRRRRRRR/# r+   r   r>   r   textr.   r`   r   )r0   s   "r   r1   r   }  s.     V VC Vs V# V$ Vr   c                f    V P                  V P                  W4      VP                  R4      4       R# )z=Write the sidecar text served at ``metadata_url``. Immutable.rS   N)r   r   rY   r5   r   r   r  s   &&&&r   put_metadataOnDiskCache.put_metadata}  s$    D''>G@TUr   c               $    V ^8  d   QhRRRRRR/# r+   r   r>   r   r.   z$tuple[str | None, str | None] | Noner   )r0   s   "r   r1   r     s$      %(	-r   c                "   V P                  W4      p VP                  4       p \        P                  ! T4      pTR,          TR,          3#   \         d     R# i ; i  \
        \        \        3 d    \        P                  RT4        R# i ; i)zReturn the cached ``(pkg_info, pyproject_toml)`` pair, or ``None`` on miss.

Written as one record, so a hit is always the complete pair. A hit
whose ``pyproject_toml`` is ``None`` means the sdist ships no
pyproject.toml, which is not the same as a miss.
Npkg_info	pyprojectz@Corrupt sdist cache record %s: not parseable; treating as a miss)
r   r   r   jsonloadsrl   KeyError	TypeErrorr   r   )r5   r   r   r]   r   docs   &&&   r   get_sdist_filesOnDiskCache.get_sdist_files  s     1	//#C	**S/C
OS%566	  		
 Hi0 	NNR 	s"   A 'A AA,BBc          
     ,    V ^8  d   QhRRRRRRRRRR/# 	r+   r   r>   r   r  r&   pyproject_tomlr.   r`   r   )r0   s   "r   r1   r     s<     
 

 
 	

 #
 

r   c           	         V P                  V P                  W4      \        P                  ! RVRV/4      P	                  R4      4       R# )z<Write the ``(pkg_info, pyproject_toml)`` pair as one record.r  r  rS   N)r   r   r  dumpsrY   r5   r   r   r  r  s   &&&&&r   put_sdist_filesOnDiskCache.put_sdist_files  sA     	W.JJ
Hk>JKRR	
r   c                    V ^8  d   QhRRRR/# r   r   )r0   s   "r   r1   r     s      C ,> r   c                    V P                  V4      p VP                  4       p\        T4      pTf   \        P                  RT4       T#   \         d     R# i ; i)DReturn the freshness policy of a cached name-level 404, or ``None``.NzBCorrupt negative cache entry %s: not decodable; treating as a miss)r   r   r   r   r   r   )r5   r   neg_path	neg_bytesr   s   &&   r   get_negativeOnDiskCache.get_negative  sa    >>'*	 ++-I  	*>NNT   		s   A
 
AAc               $    V ^8  d   QhRRRRRR/# r   r   )r0   s   "r   r1   r     s&     E EC E E Er   c                Z    V P                  V P                  V4      \        V4      4       R# zBRecord that ``package`` returned a name-level 404 from this index.N)r   r   r   r5   r   r   s   &&&r   put_negativeOnDiskCache.put_negative  s    DNN7+^F-CDr   c                    V ^8  d   QhRRRR/# r+   r   r>   r.   r`   r   )r0   s   "r   r1   r     s     < <S <T <r   c                    \        \        4      ;_uu_ 4        V P                  V4      P                  RR7       RRR4       R#   + '       g   i     R# ; i)zRemove any negative entry for ``package``.

A miss is not an error, and neither is a root that refuses the
unlink: there is nothing cached to contradict the fresh listing
the caller just fetched.
T)
missing_okN)r   r   r   unlinkr   s   &&r   drop_negativeOnDiskCache.drop_negative  s8     gNN7#**d*; s   "AA	c                   V ^8  d   QhRR/# r+   r.   z
list[Path]r   )r0   s   "r   r1   r     s      
 r   c                	p     \        V P                  P                  4       4      #   \         d    . u # i ; ir9   )listr   iterdirr   r5   s   &r   _root_childrenOnDiskCache._root_children  s3    	

**,-- 	I	s   "% 55c                   V ^8  d   QhRR/# r3  r   )r0   s   "r   r1   r     s     
 
j 
r   c                    V P                  4        Uu. uF"  p\        VP                  4      '       g   K   VNK$  	  up# u upi )zReturn the recognized bucket entries directly under the root.

Symlinks are included so the caller can decide how to handle one
rather than following it out of the root.
)r8  rM   r=   r5   childs   & r   _bucket_dirsOnDiskCache._bucket_dirs  s=      $224
4e8LUZZ8XEE4
 	
 
   >>c                   V ^8  d   QhRR/# r3  r   )r0   s   "r   r1   r     s     
 
J 
r   c                    V P                  4        Uu. uF"  p\        VP                  4      '       g   K   VNK$  	  up# u upi )zReturn the bucket entries holding records nab wrote.

The source buckets are excluded: they hold upstream files, not
nab records.
)r8  rI   r=   r<  s   & r   _entry_bucket_dirsOnDiskCache._entry_bucket_dirs  s=      $224
4e8H8TEE4
 	
 
r@  c                   V ^8  d   QhRR/# r+   r.   zIterator[Path]r   )r0   s   "r   r1   r     s     $ $N $r   c              #  L  "   V P                  4        F  pVP                  4       '       g   VP                  4       '       g   K1  \        P                  ! VRR7       F>  w  r#p\        V4      pV F'  pWV,          pVP                  4       '       d   K#  Vx  K)  	  K@  	  K  	  R# 5i)zYield each entry file inside the record buckets.

A symlinked bucket or a symlinked file is skipped, never followed
out of the tree.
F)followlinksN)rC  rr   is_dirr{   r|   r   )r5   bucketr~   	_dirnamesr   baser=   entrys   &       r   iter_cache_entriesOnDiskCache.iter_cache_entries  s      --/F  ""&--//13U1S-IG}%D KE ++--# & 2T 0s   ?B$AB$B$c                    V ^8  d   QhRRRR/# r+   r]   r   r.   r&   r   )r0   s   "r   r1   r     s      T j r   c                J    VP                  4       pTP                  pTR9   d   \	        T4      e   R# R# TR8X  d   \        T4      # TR8X  d   \        T4      # TR8X  d   T P                  Y4      # R#   \         d$   pRTP                  ;'       g    T 2u Rp?# Rp?ii ; i)ab  Return a corruption reason for a cache entry, or ``None`` if it parses.

Parses by suffix, matching each kind's read path: ``.policy`` and
``.neg`` decode as a policy, ``.metadata`` as UTF-8, ``.json`` as
JSON (an sdist record also carries its two fields), ``.parsed`` as a
parsed-listing blob. Any other suffix is not a nab entry and is
reported clean.
zunreadable: Nzpolicy not decodabler   r   r   )r   r   )r   r   strerrorsuffixr   _read_parsed_reason_read_metadata_reason_read_json_reason)r5   r]   r   r   rT  s   &&   r   read_cache_entryOnDiskCache.read_cache_entry  s    	8//#C (()#.:4V@VVY&s++[ (--W))$44  	8!#,,"5"5#!677	8s   A4 4B"?BB"B"c               $    V ^8  d   QhRRRRRR/# )r+   r]   r   r   r_   r.   r&   r   )r0   s   "r   r1   r     s!     
 
d 
 
: 
r   c                	     \         P                  ! V4      pT P                  T4      pTP	                  R4      '       d'   \        T\        4      '       d   RT9   d   RT9   g   R# R#   \         d     R # i ; i)znot valid JSONr   r  r  zsdist record missing fieldsN)r  r  rl   
_bucket_ofrA   
isinstancedict)r5   r]   r   r  rJ  s   &&&  r   rW  OnDiskCache._read_json_reason  sj    	$**S/C &X&&sD!!jC&7K3<N0  	$#	$s   A( (A76A7c                    V ^8  d   QhRRRR/# )r+   r]   r   r.   r>   r   )r0   s   "r   r1   r     s     1 1t 1 1r   c                	     VP                  V P                  4      pTP                  '       d   TP                  ^ ,          # R #   \         d     R # i ; i)rj   )relative_tor   rl   parts)r5   r]   rels   && r   r\  OnDiskCache._bucket_of  sL    	""4::.C  #yyysyy|0b0  		s   A AAc                   V ^8  d   QhRR/# r+   r.   z	list[str]r   )r0   s   "r   r1   r     s      Y r   c                   . pV P                  4        Fu  pVP                  4       '       d   VP                  4        M0VP                  4       '       d    \        P
                  ! V4       MKZ  VP                  VP                  4       Kw  	  V#   \         d%    \        T4       \        P
                  ! T4        LOi ; i)zRemove the recognized bucket directories in full, returning their names.

A symlinked bucket has its link removed, never followed, so a
target outside the root survives. A recognized-named plain file is
left in place and not counted.
)
r>  rr   r/  rI  shutilrmtreePermissionErrorr   appendr=   )r5   removedrJ  s   &  r   clear_cacheOnDiskCache.clear_cache  s      '')F  ""*MM&)
 NN6;;' *  ' *#F+MM&)*s   B,B=<B=)r   r   r   r   r   r   r   r   N)%r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r#  r)  r0  r8  r>  rC  rN  rX  rW  r\  rn  r   r   r   r   r   r      s    #
 .A-J-J#0"60M
	K"*9(	
8(V0
E<

$ 0
1 r   c                    V ^8  d   QhRRRR/# r+   r   r_   r.   r&   r   )r0   s   "r   r1   r1   1  s      u  r   c                N     V P                  R 4       R#   \         d     R# i ; i)rS   znot valid UTF-8N)r   r   r   s   &r   rV  rV  1  s,    !

7   ! !s    $$c                    V ^8  d   QhRRRR/# rq  r   )r0   s   "r   r1   r1   9  s     # #U #z #r   c                    \        V 4      # r9   )_parsed_corruptionrs  s   &r   rU  rU  9  s     c""r   c                    V ^8  d   QhRRRR/# )r+   r   r   r.   r_   r   )r0   s   "r   r1   r1   @  s     + +; +5 +r   c                    R V P                   RV P                  RV P                  RV P                  /pV P                  e   V P                  VR&   \
        P                  ! V4      P                  R4      # )r$   r%   r'   r(   r)   rS   )r$   r%   r'   r(   r)   r  r  rY   )r   r  s   & r   r   r   @  sf    f''6>>FOO	C %#//M::c?!!'**r   c                    V ^8  d   QhRRRR/# )r+   r   objectr.   r&   r   )r0   s   "r   r1   r1   N  s     ? ?F ?z ?r   c                F    \        V \        4      '       d   V '       d   V # R# )zFPage URL from a decoded policy, or None when it is unusable as a base.N)r]  r>   )r   s   &r   _policy_page_urlr|  N  s    uc**u5>$>r   c                    V ^8  d   QhRRRR/# )r+   r   r_   r.   r   r   )r0   s   "r   r1   r1   S  s       += r   c           
     6    \         P                  ! V 4      p\        \        VR ,          4      \        VR,          4      VP	                  R4      \        VP	                  R4      4      VP	                  R4      R7      #   \        \        \        3 d     R# i ; i)r$   r%   r'   r(   r)   )r$   r%   r'   r(   r)   N)	r  r  r   r#   getr|  rl   r  r  )r   r  s   & r   r   r   S  s~    
jj&3|,-I'%cggj&9:.
 	
 ), s   A;A> >BBc                      ] tR tRtRtR R ltR R ltR R ltR	 R
 ltR R lt	R R lt
R R ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R  ltR! R" ltR# R$ ltR%tR&# )'r   ia  z?Protocol shared by :class:`OnDiskCache` and :class:`NullCache`.c                    V ^8  d   QhRRRR/# r   r   )r0   s   "r   r1   CacheBackend.__annotate__d  s      # *J r   c                    R# )r   Nr   r   s   &&r   r   CacheBackend.get_simpled      r   c               (    V ^8  d   QhRRRRRRRR/# r   r   )r0   s   "r   r1   r  h  s(     	 	# 	U 	K 	J 	r   c                    R# )a  Store a Simple API body and its freshness policy; return the digest.

The returned value is the sha256 hex of the body the backend stored,
the binding key for the parsed-listing blob the caller writes next, or
``None`` when the backend stored nothing. A caller writes a parsed blob
only for a digest it was given, so no derived entry ever claims to
describe a body the store does not hold.
Nr   r5   r   r   r   s   &&&&r   r   CacheBackend.put_simpleh  s     	r   c               $    V ^8  d   QhRRRRRR/# r   r   )r0   s   "r   r1   r  s  s!      S + $ r   c                    R# )zCUpdate the policy for an existing entry without rewriting the body.Nr   r(  s   &&&r   r   "CacheBackend.refresh_simple_policys  r  r   c                    V ^8  d   QhRRRR/# r   r   )r0   s   "r   r1   r  w  s       1C r   c                    R# )zGReturn a Simple entry's freshness policy without its body, or ``None``.Nr   r   s   &&r   r   CacheBackend.get_simple_policyw  r  r   c                    V ^8  d   QhRRRR/# r   r   )r0   s   "r   r1   r  {  s        r   c                    R# )zCReturn the opaque parsed-listing blob for ``package``, or ``None``.Nr   r   s   &&r   r   CacheBackend.get_simple_parsed{  r  r   c                    V ^8  d   QhRRRR/# r   r   )r0   s   "r   r1   r    s      c j r   c                    R# )zDReturn the parsed-listing blob's on-disk size in bytes, or ``None``.Nr   r   s   &&r   r   #CacheBackend.get_simple_parsed_size  r  r   c               $    V ^8  d   QhRRRRRR/# r   r   )r0   s   "r   r1   r    s!       E d r   c                    R# )z5Store the opaque parsed-listing blob for ``package``.Nr   r   s   &&&r   r   CacheBackend.put_simple_parsed  r  r   c               $    V ^8  d   QhRRRRRR/# r   r   )r0   s   "r   r1   r    s!      C s z r   c                    R# )zAReturn the cached sidecar text for ``metadata_url``, or ``None``.Nr   r5   r   r   s   &&&r   r  CacheBackend.get_metadata  r  r   c               (    V ^8  d   QhRRRRRRRR/# r  r   )r0   s   "r   r1   r    s(      C s # $ r   c                    R# )z=Store the sidecar text served at ``metadata_url``. Immutable.Nr   r  s   &&&&r   r  CacheBackend.put_metadata  r  r   c               $    V ^8  d   QhRRRRRR/# r  r   )r0   s   "r   r1   r    s$      %(	-r   c                    R# )zCReturn the cached ``(pkg_info, pyproject_toml)`` pair, or ``None``.Nr   r5   r   r   s   &&&r   r  CacheBackend.get_sdist_files  s     	r   c          
     ,    V ^8  d   QhRRRRRRRRRR/# r  r   )r0   s   "r   r1   r    s<        	
 # 
r   c                    R# )z<Store the ``(pkg_info, pyproject_toml)`` pair as one record.Nr   r  s   &&&&&r   r  CacheBackend.put_sdist_files  s     	r   c                    V ^8  d   QhRRRR/# r   r   )r0   s   "r   r1   r    s      C ,> r   c                    R# )r   Nr   r   s   &&r   r#  CacheBackend.get_negative  r  r   c               $    V ^8  d   QhRRRRRR/# r   r   )r0   s   "r   r1   r    s!      C   r   c                    R# r'  r   r(  s   &&&r   r)  CacheBackend.put_negative  r  r   c                    V ^8  d   QhRRRR/# r,  r   )r0   s   "r   r1   r    s      S T r   c                    R# )z*Remove any negative entry for ``package``.Nr   r   s   &&r   r0  CacheBackend.drop_negative  r  r   c                   V ^8  d   QhRR/# rF  r   )r0   s   "r   r1   r    s      N r   c                    R# )z0Yield each entry file inside the record buckets.Nr   r7  s   &r   rN  CacheBackend.iter_cache_entries  r  r   c                    V ^8  d   QhRRRR/# rQ  r   )r0   s   "r   r1   r    s      T j r   c                    R# )zGReturn a corruption reason for a cache entry, or ``None`` if it parses.Nr   r5   r]   s   &&r   rX  CacheBackend.read_cache_entry  r  r   c                   V ^8  d   QhRR/# rg  r   )r0   s   "r   r1   r    s      Y r   c                    R# )zFRemove the recognized bucket directories, returning the names removed.Nr   r7  s   &r   rn  CacheBackend.clear_cache  r  r   r   Nr   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r#  r)  r0  rN  rX  rn  r   r   r   r   r   r   a  s_    I	 r   c                      ] tR tRtRtR R ltR R ltR R ltR	 R
 ltR R lt	R R lt
R R ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R  ltR! R" ltR# R$ ltR%tR&# )'r   i  aw  No-op cache backend used when persistence is disabled.

Lets :class:`CachedAsyncSimpleClient` be used unconditionally so
the call site does not branch on whether a cache is configured.
Each method is a docstring-only stub: gets implicitly return
``None`` (a permanent miss) and puts implicitly do nothing.
Argument names match :class:`CacheBackend` for Protocol conformance.
c                    V ^8  d   QhRRRR/# r   r   )r0   s   "r   r1   NullCache.__annotate__  s     / /# /*J /r   c                    R# z Return ``None`` (always a miss).Nr   r   s   &&r   r   NullCache.get_simple      r   c               (    V ^8  d   QhRRRRRRRR/# )	r+   r   r>   r   r_   r   r   r.   r`   r   )r0   s   "r   r1   r    s(      # U K D r   c                    R# )zDiscard the entry; return ``None`` since no body was stored.

A disabled cache holds nothing for a parsed blob to describe, so the
caller skips building one rather than encoding records into a store
that would drop them.
Nr   r  s   &&&&r   r   NullCache.put_simple  r  r   c               $    V ^8  d   QhRRRRRR/# r   r   )r0   s   "r   r1   r    s!     * *S *+ *$ *r   c                    R# )zDiscard the policy refresh.Nr   r(  s   &&&r   r   NullCache.refresh_simple_policy  r  r   c                    V ^8  d   QhRRRR/# r   r   )r0   s   "r   r1   r    s     / / /1C /r   c                    R# r  r   r   s   &&r   r   NullCache.get_simple_policy  r  r   c                    V ^8  d   QhRRRR/# r   r   )r0   s   "r   r1   r    s     / / / /r   c                    R# r  r   r   s   &&r   r   NullCache.get_simple_parsed  r  r   c                    V ^8  d   QhRRRR/# r   r   )r0   s   "r   r1   r    s     / /c /j /r   c                    R# r  r   r   s   &&r   r    NullCache.get_simple_parsed_size  r  r   c               $    V ^8  d   QhRRRRRR/# r   r   )r0   s   "r   r1   r    s!     ! ! !E !d !r   c                    R# zDiscard the entry.Nr   r   s   &&&r   r   NullCache.put_simple_parsed  r  r   c               $    V ^8  d   QhRRRRRR/# r   r   )r0   s   "r   r1   r    s!     / /C /s /z /r   c                    R# r  r   r  s   &&&r   r  NullCache.get_metadata  r  r   c               (    V ^8  d   QhRRRRRRRR/# r  r   )r0   s   "r   r1   r    s(     ! !C !s !# !$ !r   c                    R# r  r   r  s   &&&&r   r  NullCache.put_metadata  r  r   c               $    V ^8  d   QhRRRRRR/# r  r   )r0   s   "r   r1   r    s$     / //%(/	-/r   c                    R# r  r   r  s   &&&r   r  NullCache.get_sdist_files  r  r   c          
     ,    V ^8  d   QhRRRRRRRRRR/# r  r   )r0   s   "r   r1   r    s<     ! !! ! 	!
 #! 
!r   c                    R# r  r   r  s   &&&&&r   r  NullCache.put_sdist_files  r  r   c                    V ^8  d   QhRRRR/# r   r   )r0   s   "r   r1   r    s     / /C /,> /r   c                    R# r  r   r   s   &&r   r#  NullCache.get_negative  r  r   c               $    V ^8  d   QhRRRRRR/# r   r   )r0   s   "r   r1   r    s!     ! !C ! ! !r   c                    R# r  r   r(  s   &&&r   r)  NullCache.put_negative  r  r   c                    V ^8  d   QhRRRR/# r,  r   )r0   s   "r   r1   r    s      S T r   c                    R# )zDo nothing.Nr   r   s   &&r   r0  NullCache.drop_negative  r  r   c                   V ^8  d   QhRR/# rF  r   )r0   s   "r   r1   r    s      N r   c                    \        R4      # )z&Yield nothing (no persistent entries).r   )iterr7  s   &r   rN  NullCache.iter_cache_entries  s    Bxr   c                    V ^8  d   QhRRRR/# rQ  r   )r0   s   "r   r1   r    s     N NT Nj Nr   c                    R# )z?Return ``None`` (a disabled cache never holds a corrupt entry).Nr   r  s   &&r   rX  NullCache.read_cache_entry  r  r   c                   V ^8  d   QhRR/# rg  r   )r0   s   "r   r1   r     s      Y r   c                    . # )z)Return an empty list (nothing to remove).r   r7  s   &r   rn  NullCache.clear_cache   s    	r   r   Nr  r   r   r   r   r     sb    /*///!/!/
!/!N r   )ARCHIVE_BUCKET
VCS_BUCKETr   r   r   r   r   rM   )r   r   r   )>r   
__future__r   rW   r  loggingr{   ri  rt   r4   
contextlibr   dataclassesr   r   pathlibr   typingr   r   atomicr	   parsed_listingr
   rv  r   r   collections.abcr   	getLoggerr   r   __all__r   r   r   r   r   rG   r  r  rL   	frozensetrV   	Exceptionr   r   rI   rM   r[   rf   rn   rv   r   r   rV  rU  r   r|  r   r   r   r   r   r   <module>r     s?  "H #    	     *  *   C .(			8	$	  "     ;  
n-!  L9 L $d#8 8 $86L
<
F+?"q qh#+?
T8 TnJ Jr   