o
    !2gs                     @   s  U 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	Z	d dl
Z
d dlmZ d dlmZ d dlmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm Z m!Z! zd dl"m#Z$ d dl"m%Z& W n   e
'd dd	 Z$ej%Z&Y d dl(Z(d
dl)m*Z* d
dl+m,Z, d
dl-m.Z. d
dl/m0Z0 d
dl1m2Z2 d
dl3m4Z4m5Z5 d
dl6m7Z7m8Z8 ej9:e;Z<e=e>d< eee=f Z?ed Z@G dd dZAdejBfddZCe,e.dZDee@ee* f e>d< edZEe	jFdd dkser"e	jFdd dkrd dlmGZG nd dlHmGZG eGdZIeeIeEf ZJee?eeIe?f f ZKeeIe0f ZLnedZIeZJeeIef ZKeeIef ZLed eJd!ZMde0fd"d#ZNd$ede0fd%d&ZOe=ZPed' ZQeeQee f ZRdeePeRf fd(d)ZSdeTfd*d+ZUd,d- ZVd.eWd/ejBddfd0d1ZXeeeY ZZeVdeZfd2deNdd3ddd4d5eeKeI  d6eTd7eeeeeQef f  d8eLeI d/eejB d9e[d:ee= d;ee@ fd<d=Z\er	e!d>eMdeMfd?d@Z]e!	AdYdAdAdAdAdAdAdAd4d5eeKeI  d6eTd7eeeeeQef f  d8eLeI d/eejB d9e[d:ee= d;ee@ deeMgeMf fdBd@Z]ne\Z]dCede=fdDdEZ^dCedee= fdFdGZ_dZd/eejB dee= fdHdIZ`dJe=dKe=deTfdLdMZadJe=d/ejBdeTfdNdOZbdPZcd:ZddQZedRZfeG dSdT dTeeI ZgdUegeI fdVdWZhg dXZidS )[    N)	dataclass)Path)TYPE_CHECKINGAnyCallableDictGenericIterableListLiteralOptionalTupleTypeTypeVarUnioncastget_args
get_originget_type_hintsoverload)dumps)loadszUorjson couldn't be imported. It's _highly_ recommended for better caching performancec                  O   s   t j| i |dS )Nutf8)jsonr   encodeargskwargs r   G/home/garg/my-data/venv/lib/python3.10/site-packages/cachew/__init__.pyorjson_dumps)   s   r       )AbstractBackend)FileBackend)SqliteBackend)
SourceHash)make_logger)CachewMarshallbuild_schema)CachewExceptionTypeNotSupportedCACHEW_VERSION)sqlitefilec                   @   sP   e Zd ZU dZ	 dZeed< ee	dZ
eed< 	 dZeed< dZeed	< d
S )settingszG
    Global settings, you can override them after importing cachew
    TENABLEcachewDEFAULT_CACHEW_DIRFTHROW_ON_ERRORr,   DEFAULT_BACKENDN)__name__
__module____qualname____doc__r/   bool__annotations__r   appdirsuser_cache_dirr1   PathIshr2   r3   Backendr   r   r   r   r.   D   s   
 r.   returnc                   C   s   t tS N)r&   r4   r   r   r   r   
get_loggerX   s   r@   )r-   r,   BACKENDSR   )   
   )	ParamSpecPF)boundc                  O   s   t | tt|  S r?   )strtuplesorteditemsr   r   r   r   default_hashw   s   rN   pathc                 O   s*   |   j}t|  d| g|R i |S )N.)statst_mtimerN   )rO   r   r   mtr   r   r   
mtime_hash~   s   
 rT   )singlemultiplec              
      s  zt | }W n ty } z
t|W  Y d}~S d}~ww |dd  du r,d|  S dtdtf fdd}t }|rZt }|du rI|dS t|dkrV|d	| S |\}n }zt|d
 W n ty} } z|d|j	 W  Y d}~S d}~ww |rd|fS d|fS )a	  
    >>> def const() -> int:
    ...     return 123
    >>> infer_return_type(const)
    ('single', <class 'int'>)

    >>> from typing import Optional
    >>> def first_character(s: str) -> Optional[str]:
    ...     return None if len(s) == 0 else s[0]
    >>> kind, opt = infer_return_type(first_character)
    >>> # in 3.8, Optional[str] is printed as Union[str, None], so need to hack around this
    >>> (kind, opt is Optional[str])
    ('single', True)

    # tuple is an iterable.. but presumably should be treated as a single value
    >>> from typing import Tuple
    >>> def a_tuple() -> Tuple[int, str]:
    ...     return (123, 'hi')
    >>> infer_return_type(a_tuple)
    ('single', typing.Tuple[int, str])

    >>> from typing import Collection, NamedTuple
    >>> class Person(NamedTuple):
    ...     name: str
    ...     age: int
    >>> def person_provider() -> Collection[Person]:
    ...     return []
    >>> infer_return_type(person_provider)
    ('multiple', <class 'cachew.Person'>)

    >>> def single_str() -> str:
    ...     return 'hello'
    >>> infer_return_type(single_str)
    ('single', <class 'str'>)

    >>> def single_person() -> Person:
    ...     return Person(name="what", age=-1)
    >>> infer_return_type(single_person)
    ('single', <class 'cachew.Person'>)

    >>> from typing import Sequence
    >>> def int_provider() -> Sequence[int]:
    ...     return (1, 2, 3)
    >>> infer_return_type(int_provider)
    ('multiple', <class 'int'>)

    >>> from typing import Iterator, Union
    >>> def union_provider() -> Iterator[Union[str, int]]:
    ...     yield 1
    ...     yield 'aaa'
    >>> infer_return_type(union_provider)
    ('multiple', typing.Union[str, int])

    # a bit of an edge case
    >>> from typing import Tuple
    >>> def empty_tuple() -> Iterator[Tuple[()]]:
    ...     yield ()
    >>> infer_return_type(empty_tuple)
    ('multiple', typing.Tuple[()])

    ... # doctest: +ELLIPSIS

    >>> def untyped():
    ...     return 123
    >>> infer_return_type(untyped)
    'no return type annotation...'

    >>> from typing import List
    >>> class Custom:
    ...     pass
    >>> def unsupported() -> Custom:
    ...     return Custom()
    >>> infer_return_type(unsupported)
    "can't infer type from <class 'cachew.Custom'>: can't cache <class 'cachew.Custom'>"

    >>> def unsupported_list() -> List[Custom]:
    ...     return [Custom()]
    >>> infer_return_type(unsupported_list)
    "can't infer type from typing.List[cachew.Custom]: can't cache <class 'cachew.Custom'>"
    Nr>   zno return type annotation on reasonc                    s   d  d|  S )Nzcan't infer type from z: r   )rW   rtyper   r   bail   s   zinfer_return_type.<locals>.bailzhas no __args__r!   zwrong number of __args__: )r   zcan't cache rV   rU   )
r   	ExceptionrJ   get_returns_multipler   lenr(   r*   type_)funchintsnerZ   return_multipler   cached_typeexr   rX   r   infer_return_type   s4   Q
rf   c                 C   sB   t | }|d u r
dS |tu rdS zt|tW S  ty    Y dS w )NF)r   rK   
issubclassr	   	TypeError)rY   originr   r   r   r]      s   r]   c                    s   t   fdd}|S )Nc                     s@   t  dkrt dkrt d r d S  fddS )Nr!   r   c                    s   | g R i S r?   r   )realf)r   fr   r   r   <lambda>  s    z-doublewrap.<locals>.new_dec.<locals>.<lambda>)r^   callabler   rk   r   r   new_dec  s   $zdoublewrap.<locals>.new_dec)	functoolswraps)rk   ro   r   rn   r   
doublewrap  s   rr   eloggerc                C   s"   t jr| |d ||  d S )Nz@error while setting up cache, falling back to non-cached version)r.   r2   error	exception)rs   rt   r   r   r   cachew_error  s   
rw   Fd   )
force_filecls
depends_onrt   chunk_bysynthetic_keybackend
cache_pathry   rz   r{   r|   r}   r~   c                   s"  |du rt dd}
|
dur|
tjjjv rt|
}nt }G dd dtj}t}||d|i}t	tj|}|	
dd}|durItd |}tjrP|du rW|d S |tu rftj}|d	|  dd}|durzt|t}W n   d
}Y |r|\}nd|}t}t|trd| d}|du rt|}t||d S || dusJ n2|\}}|du r|d| d|  ||}ndusJ |f|kr|d| d|f  dkrtfdd}n}t|||||||||d	 t fdd}|S )a  
    Database-backed cache decorator. TODO more description?
    # TODO use this doc in readme?

    :param cache_path: if not set, `cachew.settings.DEFAULT_CACHEW_DIR` will be used.
    :param force_file: if set to True, assume `cache_path` is a regular file (instead of a directory)
    :param cls: if not set, cachew will attempt to infer it from return type annotation. See :func:`infer_return_type` and :func:`cachew.tests.test_cachew.test_return_type_inference`.
    :param depends_on: hash function to determine whether the underlying . Can potentially benefit from the use of side effects (e.g. file modification time). TODO link to test?
    :param logger: custom logger, if not specified will use logger named `cachew`. See :func:`get_logger`.
    :return: iterator over original or cached items

    Usage example:
    >>> from typing import NamedTuple, Iterator
    >>> class Link(NamedTuple):
    ...     url : str
    ...     text: str
    ...
    >>> @cachew
    ... def extract_links(archive_path: str) -> Iterator[Link]:
    ...     for i in range(5):
    ...         # simulate slow IO
    ...         # this function runs for five seconds for the purpose of demonstration, but realistically it might take hours
    ...         import time; time.sleep(1)
    ...         yield Link(url=f'http://link{i}.org', text=f'text {i}')
    ...
    >>> list(extract_links(archive_path='wikipedia_20190830.zip')) # that would take about 5 seconds on first run
    [Link(url='http://link0.org', text='text 0'), Link(url='http://link1.org', text='text 1'), Link(url='http://link2.org', text='text 2'), Link(url='http://link3.org', text='text 3'), Link(url='http://link4.org', text='text 4')]

    >>> from timeit import Timer
    >>> res = Timer(lambda: list(extract_links(archive_path='wikipedia_20190830.zip'))).timeit(number=1)
    ... # second run is cached, so should take less time
    >>> print(f"call took {int(res)} seconds")
    call took 0 seconds

    >>> res = Timer(lambda: list(extract_links(archive_path='wikipedia_20200101.zip'))).timeit(number=1)
    ... # now file has changed, so the cache will be discarded
    >>> print(f"call took {int(res)} seconds")
    call took 5 seconds
    Nr5   c                   @   s   e Zd Zdd ZdS )z cachew_impl.<locals>.AddFuncNamec                 S   s.   | j }|d us	J |d }d| d| |fS )N	func_name[z] )extra)selfmsgr   r   r   r   r   r   processm  s   z(cachew_impl.<locals>.AddFuncName.processN)r4   r5   r6   r   r   r   r   r   AddFuncNamel  s    r   r   hashfz6'hashf' is deprecated. Please use 'depends_on' insteadzJcache explicitly disabled (settings.ENABLE is False or cache_path is None)z+no cache_path specified, using the default FrV   zfailed to infer cache type: zS. See https://github.com/karlicoss/cachew#features for the list of supported types.rt   zusing inferred type  zinferred type z& mismatches explicitly specified type rU   c                     s    | i |gS r?   r   r   r`   r   r   _func  s   zcachew_impl.<locals>._func)	r`   r   ry   cls_r{   rt   r|   r}   r~   c                     sF    |d< t | i |}dkr!t|}t|dksJ ||d S |S )N_cachew_contextrU   r!   r   )cachew_wrapperlistr^   )r   r   reslres)ctxuse_kindr   r   binder  s   zcachew_impl.<locals>.binder)getattrloggingLoggermanager
loggerDict	getLoggerr@   LoggerAdaptercallable_namer   r\   warningswarnr.   r/   debuguse_default_pathr1   
isinstancerK   rf   Failurer)   rw   warningrp   rq   Context)r`   r   ry   rz   r{   rt   r|   r}   r~   r   module_namer   r   adapterr   use_clsis_tupleinference_resr   re   inferred_kindinferred_clsr   r   r   )r   r`   r   r   cachew_impl(  s   ;





r   func                 C      d S r?   r   )r   r   r   r   r0     s   r0   .c                C   r   r?   r   )r   ry   rz   r{   rt   r|   r}   r~   r   r   r   r0     s   r`   c                 C   s    t | dd pd}| d| j S )Nr5    :)r   r6   )r`   modr   r   r   r     s   r   c                 C   s   t | dd S )Nr5   )r   r   r   r   r   callable_module_name  s   r   c                 C   s`   dt jvrg S t jd }| dkrg S d|v r&| r&| d|dd  dd |dD S )NCACHEW_DISABLEr   ,zxCACHEW_DISABLE contains a comma, but this expects a $PATH-like, colon-separated list; try something like CACHEW_DISABLE=r   c                 S   s   g | ]
}|  d kr|qS )r   )strip).0pr   r   r   
<listcomp>  s    z+_parse_disabled_modules.<locals>.<listcomp>)osenvironr   r   replacesplit)rt   disabledr   r   r   _parse_disabled_modules  s   


r   r   patternc                 C   s^   | |krdS |  d}| d}t|t|k rdS t||D ]\}}t||r*q dS dS )a~  
    >>> _matches_disabled_module('my.browser', 'my.browser')
    True
    >>> _matches_disabled_module('my.browser', 'my.*')
    True
    >>> _matches_disabled_module('my.browser', 'my')
    True
    >>> _matches_disabled_module('my.browser', 'my.browse*')
    True
    >>> _matches_disabled_module('my.browser.export', 'my.browser')
    True
    >>> _matches_disabled_module('mysomething.else', '*')  # CACHEW_DISABLE='*' disables everything
    True
    >>> _matches_disabled_module('my.browser', 'my.br?????')  # fnmatch supports unix-like patterns
    True
    >>> _matches_disabled_module('my.browser', 'my.browse')
    False
    >>> _matches_disabled_module('mysomething.else', 'my')  # since not at '.' boundary, doesn't match
    False
    >>> _matches_disabled_module('mysomething.else', '')
    False
    >>> _matches_disabled_module('my.browser', 'my.browser.export')
    False
    TrP   F)r   r^   zipfnmatch)r   r   module_partspattern_partsmpppr   r   r   _matches_disabled_module  s   

r   c              
   C   sJ   t |}|D ]}t| |r"|d|  d| dtjd  d  dS qdS )Nzcaching disabled for z (matched 'z' from 'CACHEW_DISABLE=r   z)'TF)r   r   r   r   r   )r   rt   disabled_modulespatr   r   r   _module_is_disabled1  s   
$r   cachew_cachedsynthetic_key_valuedependenciesc                   @   s~   e Zd ZU eed< ee ed< eed< eed< e	e ed< e
jed< eed< ee ed< ee ed	< d
eeef fddZdS )r   r`   r   ry   r   r{   rt   r|   r}   r~   r>   c              
      s   t | j}dd |j D }t | j  fdd| D }i ||}t| j}dtd|t	t| j|i |i}| j
}|d urM||t< || |t< |S )Nc                 S   s&   i | ]\}}|j tjjur||j qS r   )defaultinspect	Parameteremptyr   kvr   r   r   
<dictcomp>S  s
    z*Context.composite_hash.<locals>.<dictcomp>c                    s*   i | ]\}}| j v sd  j v r||qS )r   )
parametersr   hsigr   r   r   Z  s
    r0   schema)r   	signaturer`   r   rM   r{   rJ   r   r+   _DEPENDENCIESr}   _SYNTHETIC_KEY_SYNTHETIC_KEY_VALUE)r   r   r   fsigdefaultsr   
hash_partsr}   r   r   r   composite_hashP  s&   

zContext.composite_hashN)r4   r5   r6   r   r9   PathProviderrG   r8   r   HashFunctionr   r   intr   rJ   r=   r   r   r   r   r   r   r   r   C  s   
 
r   r   c                 /   s   | }|j |j|j|j}|j}|j|j|j|j}|p"t	j
t	t	js;d  i 
E d H  d S t}|d urTt|rT i 
E d H  d S dtt f 	
fdd}d
fdd}	d 
fdd	}
fd
dz| d u r i 
E d H  W d S t }|j i 
td  t|d|dX d  krd  E d H  	 W d    W d S d |	   }|s i 
E d H  	 W d    W d S |
 E d H  W d    W d S 1 s!w   Y  W d S  ty] } z(rBdt|v rBW Y d }~d S t|d  i 
E d H  W Y d }~d S d }~ww )Nz4cache explicitly disabled (settings.ENABLE is False)r>   c                     s   t r i } | d u rd d S t| }nt}|jjddd z| }W n tyD   r7n|jddd | }Y nw t|jrO| }d d| d |S )Nz.cache explicitly disabled (cache_path is None)T)parentsexist_okzusing r   z
 for cache)	rm   r   r   parentmkdirrQ   FileNotFoundErrorS_ISDIRst_mode)r   db_pathst)r   r   ry   r   r   rt   used_backendr   r   get_db_path  s*   


z#cachew_wrapper.<locals>.get_db_pathc                     s   d u rd S i  d urzt  W n
 t jy   Y nw  fddg    R D } t|  }|s;d S dtt dtt dttt  fdd}t	 } t	 }|||d}|d urk t
< |< d S d S )	Nc                    s.   i | ]}|t thvr| ||kqS r   )r   r   r\   )r   r   )
new_hash_d
old_hash_dr   r   r     s
    zAcachew_wrapper.<locals>.try_use_synthetic_key.<locals>.<dictcomp>cachedwantedr>   c                 S   sj   t | dkrd S t |dkrd S | d |d krd S | d }t|D ]\}}||kr2||d    S q"d S )Nr   )r^   	enumerate)r   r   last_cachedir   r   r   r   missing_keys  s   zCcachew_wrapper.<locals>.try_use_synthetic_key.<locals>.missing_keys)r   r   )r   r   JSONDecodeErrorkeysallvaluesr
   rJ   r   r   _CACHEW_CACHED)
hash_diffscache_compatibler   
new_values
old_valuesmissing)cached_itemsr   r   old_hashr}   )r   r   try_use_synthetic_key  s.   &
z-cachew_wrapper.<locals>.try_use_synthetic_keyFc               	   3   s    i } t tr n	 jg  d fdd}d}| D ]0}z	|d7 }|V  W n ty=   dY  d S w 
|}t|} | t krU|  q%|  	 	
d| d d	 d
 d S )Nr>   c                      s"   t  dkr d g  d S d S )Nr   )chunk)r^   r   r  flush_blobsr   r   flush  s   
z7cachew_wrapper.<locals>.written_to_cache.<locals>.flushr   r!   Tzwrote   z objects to   cachew (r   )r>   N)r   r#   write_new_hashr  GeneratorExitdumpr    appendr^   finalizeinfo)datasr  total_objectsobjdctblob)r   r~   r|   r   
early_exitr`   r   rt   marshallnew_hashr   r  r   written_to_cache  s4   




"z(cachew_wrapper.<locals>.written_to_cachec               	   3   sh       } | d u rdn|  d}d| d d d   D ]}t|}|}|V  q#d S )Nr   r   zloading zobjects from cachew (r   r	  )cached_blobs_totalr  cached_blobsorjson_loadsload)total_cachedtotal_cached_sr  jr  )r~   r   rt   r  r   r   r   r    s   
z$cachew_wrapper.<locals>.cached_itemsz
new hash: )Type_)r   rt   z
old hash: z hash matched: loading from cachez/hash mismatch: computing data and writing to dbz#Cannot operate on a closed databaser   r
  )r`   r   ry   r   r{   rt   r|   r}   r~   r.   r3   r   r/   r   r   r   r   r   rA   r   r   r   r'   get_old_hashget_exclusive_writer[   rJ   rw   )r   r   r   Crz   r{   backend_namemod_namer   r  r  
BackendCls	got_writers   r   )r   r~   r   r  r|   r   r  ry   r`   r   r   rt   r  r  r   r  r}   r   r   r   q  sx   

"#6 +



	("r   )r0   r)   r%   r   r@   ).r?   )jr   rp   importlib.metadata	importlibr   r   r   r   rQ   sysr   dataclassesr   pathlibr   typingr   r   r   r   r   r	   r
   r   r   r   r   r   r   r   r   r   r   r   orjsonr   r    r   r  r   r:   backend.commonr"   backend.filer#   backend.sqliter$   commonr%   logging_helperr&   marshall.cachewr'   r(   utilsr)   r*   metadataversionr4   r+   rJ   r9   r<   r=   r.   r   r@   rA   rB   version_inforF   typing_extensionsrG   CCr   r   rH   rN   rT   r   KindInferredrf   r8   r]   rr   r[   rw   objectr   r   r   r0   r   r   r   r   r   r   r   r   r   r   r   __all__r   r   r   r   <module>   s  
 P
w
 (
	
,-
 c