o
    5ti8f                     @   s  d Z ddlZddlZddlZddlZddlZddlZddlZddlm	Z	m
Z
 ddlZdZd"ddZzddlmZmZmZ W n eyP   ddlmZmZmZ Y nw zddlmZ W n eyh   dd	lmZ Y nw zdd
lmZ W n ey   dd
lmZ Y nw eeZdZdZdZ d\Z!Z"Z#dd Z$dd Z%dd Z&dd Z'dd Z(dd Z)dd Z*G dd deZ+G dd  d ej,Z-ed!kre.e dS dS )#a9  
A lightweight wrapper around Python's sqlite3 database, with a dict-like interface
and multi-thread access support::

>>> mydict = SqliteDict('some.db', autocommit=True) # the mapping will be persisted to file `some.db`
>>> mydict['some_key'] = any_picklable_object
>>> print mydict['some_key']
>>> print len(mydict) # etc... all dict functions work

Pickle is used internally to serialize the values. Keys are strings.

If you don't use autocommit (default is no autocommit for performance), then
don't forget to call `mydict.commit()` when done with a transaction.

    N)	b64decode	b64encodez2.1.0c                 C   s&   |d u r|  }|j |ur|||N)__traceback__with_traceback)tpvaluetb r
   >/home/ubuntu/.local/lib/python3.10/site-packages/sqlitedict.pyreraise*   s
   

r   )dumpsloadsHIGHEST_PROTOCOL)UserDict)	DictMixin)Queuez	--close--z
--commit--z--no more--)r         c                 C   s8   | d ur|  }|d u rt }~|S || t}~|S tS r   )_PUT_REFERENT_DESTROYEDput_PUT_OK	_PUT_NOOP)queue_referenceitemqueueretvalr
   r
   r   _put^   s   
r   c                  O   s   t | i |S )z*See documentation of the SqliteDict class.)
SqliteDict)argskwargsr
   r
   r   opens   s   r!   c                 C   s   t t| tdS )zGSerialize an object using pickle to a binary format accepted by SQLite.protocol)sqlite3Binaryr   PICKLE_PROTOCOLobjr
   r
   r   encodex      r)   c                 C   s   t t| S )z*Deserialize objects retrieved from SQLite.)r   bytesr'   r
   r
   r   decode}   s   r,   c                 C   s   t t| tddS )zJSerialize a key using pickle + base64 encoding to text accepted by SQLite.r"   ascii)r   r   r&   r,   keyr
   r
   r   
encode_key   s   r0   c                 C   s   t t| dS )z(Deserialize a key retrieved from SQLite.r-   )r   r   r)   r.   r
   r
   r   
decode_key   r*   r1   c                 C   s   | S )z1Identity f(x) = x function for encoding/decoding.r
   r'   r
   r
   r   identity   s   r2   c                   @   s
  e Zd Zg dZdddddeeeeddfd	d
Zdd Zdd Z	dd Z
dd Zdd Zdd Zdd Zdd Zdd Zdd Zdd  Zd!d" Zd#d$ Zd%d& Zd'd( Zd)d* Zd+d, Zd>d.d/Zd0d1 Zd2d3 Zed4d5 Zd?d6d7ZeZd@d8d9Z d:d; Z!d<d= Z"dS )Ar   )crwnNunnamedr3   FDELETE   Tc                 C   sL  |du | _ | j rtjdd\}}t| |tjvr td| || _|dkr2tj	
|r2t| tj	|}|rFtj	
|sFtd| || _|dd| _|| _|| _|| _|| _|| _|	| _|| _td	||f  |  | _| jd
kr| jt| jvrd| }t|nd| j }| j| | j  |dkr|   dS dS )a8  
        Initialize a thread-safe sqlite-backed dictionary. The dictionary will
        be a table `tablename` in database file `filename`. A single file (=database)
        may contain multiple tables.

        If no `filename` is given, a random file in temp will be used (and deleted
        from temp once the dict is closed/deleted).

        If you enable `autocommit`, changes will be committed after each operation
        (more inefficient but safer). Otherwise, changes are committed on `self.commit()`,
        `self.clear()` and `self.close()`.

        Set `journal_mode` to 'OFF' if you're experiencing sqlite I/O problems
        or if you need performance and don't care about crash-consistency.

        Set `outer_stack` to False to disable the output of the outer exception
        to the error logs.  This may improve the efficiency of sqlitedict
        operation at the expense of a detailed exception trace.

        The `flag` parameter. Exactly one of:
          'c': default mode, open for read/write, creating the db/table if necessary.
          'w': open for r/w, but drop `tablename` contents first (start with empty table)
          'r': open as read-only
          'n': create a new database (erasing any existing tables, not just `tablename`!).

        The `encode` and `decode` parameters are used to customize how the values
        are serialized and deserialized.
        The `encode` parameter must be a function that takes a single Python
        object and returns a serialized representation.
        The `decode` function must be a function that takes the serialized
        representation produced by `encode` and returns a deserialized Python
        object.
        The default is to use pickle.

        The `timeout` defines the maximum time (in seconds) to wait for initial Thread startup.

        Nsqldict)prefixzUnrecognized flag: %sr6   z'Error! The directory does not exist, %s"z""zopening Sqlite table %r in %rr4   z8Refusing to create a new table "%s" in read-only DB modezBCREATE TABLE IF NOT EXISTS "%s" (key TEXT PRIMARY KEY, value BLOB)r5   )in_temptempfilemkstemposcloser   VALID_FLAGSRuntimeErrorflagpathexistsremovedirnamefilenamereplace	tablename
autocommitjournal_moder)   r,   r0   r1   _outer_stackloggerdebug	_new_connconnget_tablenamesexecutecommitclear)selfrI   rK   rD   rL   rM   r)   r,   r0   r1   timeoutouter_stackfdrH   msg
MAKE_TABLEr
   r
   r   __init__   sH   
)






zSqliteDict.__init__c                 C   s   t | j| j| j| jdS )N)rL   rM   rY   )SqliteMultithreadrI   rL   rM   rN   rW   r
   r
   r   rQ      s   zSqliteDict._new_connc                 C   s"   t | dr
| jd u r|  | _| S )NrR   )hasattrrR   rQ   r_   r
   r
   r   	__enter__   s   
zSqliteDict.__enter__c                 G   s   |    d S r   )rA   )rW   exc_infor
   r
   r   __exit__   s   zSqliteDict.__exit__c                 C   s
   d| j  S )NzSqliteDict(%s))rI   r_   r
   r
   r   __str__   s   
zSqliteDict.__str__c                 C   s   t | S r   )strr_   r
   r
   r   __repr__      zSqliteDict.__repr__c                 C   s*   d| j  }| j|d }|d ur|S dS )NzSELECT COUNT(*) FROM "%s"r   rK   rR   
select_one)rW   GET_LENrowsr
   r
   r   __len__   s   
zSqliteDict.__len__c                 C   s*   d| j  }| j|d }|d urdS dS )NzSELECT MAX(ROWID) FROM "%s"r   TFrh   )rW   GET_MAXmr
   r
   r   __bool__
  s   
zSqliteDict.__bool__c                 c   2    d| j  }| j|D ]
}| |d V  qd S )Nz#SELECT key FROM "%s" ORDER BY rowidr   )rK   rR   selectr1   )rW   GET_KEYSr/   r
   r
   r   iterkeys  
   
zSqliteDict.iterkeysc                 c   rp   )Nz%SELECT value FROM "%s" ORDER BY rowidr   )rK   rR   rq   r,   )rW   
GET_VALUESr   r
   r
   r   
itervalues  rt   zSqliteDict.itervaluesc                 c   s<    d| j  }| j|D ]\}}| || |fV  qd S )Nz*SELECT key, value FROM "%s" ORDER BY rowid)rK   rR   rq   r1   r,   )rW   	GET_ITEMSr/   r   r
   r
   r   	iteritems  s
   
zSqliteDict.iteritemsc                 C      |   S r   rs   r_   r
   r
   r   keys   rg   zSqliteDict.keysc                 C   ry   r   )rv   r_   r
   r
   r   values#  rg   zSqliteDict.valuesc                 C   ry   r   )rx   r_   r
   r
   r   items&  rg   zSqliteDict.itemsc                 C   s$   d| j  }| j|| |fd uS )Nz SELECT 1 FROM "%s" WHERE key = ?)rK   rR   ri   r0   )rW   r/   HAS_ITEMr
   r
   r   __contains__)  s   
zSqliteDict.__contains__c                 C   s>   d| j  }| j|| |f}|d u rt|| |d S )Nz$SELECT value FROM "%s" WHERE key = ?r   )rK   rR   ri   r0   KeyErrorr,   )rW   r/   GET_ITEMr   r
   r
   r   __getitem__-  s
   
zSqliteDict.__getitem__c                 C   sP   | j dkr	tdd| j }| j|| || |f | jr&|   d S d S )Nr4   z)Refusing to write to read-only SqliteDictz+REPLACE INTO "%s" (key, value) VALUES (?,?))	rD   rC   rK   rR   rT   r0   r)   rL   rU   )rW   r/   r   ADD_ITEMr
   r
   r   __setitem__4  s   

zSqliteDict.__setitem__c                 C   sX   | j dkr	td|| vrt|d| j }| j|| |f | jr*|   d S d S )Nr4   z,Refusing to delete from read-only SqliteDictzDELETE FROM "%s" WHERE key = ?)	rD   rC   r   rK   rR   rT   r0   rL   rU   )rW   r/   DEL_ITEMr
   r
   r   __delitem__=  s   

zSqliteDict.__delitem__r
   c                    s    j dkr	tdz| }W n	 ty   Y nw  fdd|D }d j } j|| |r5 |  jr> 	  d S d S )Nr4   z'Refusing to update read-only SqliteDictc                    s$   g | ]\}}  | |fqS r
   )r0   r)   ).0kvr_   r
   r   
<listcomp>P  s   $ z%SqliteDict.update.<locals>.<listcomp>z,REPLACE INTO "%s" (key, value) VALUES (?, ?))
rD   rC   r}   AttributeErrorrK   rR   executemanyupdaterL   rU   )rW   r}   kwdsUPDATE_ITEMSr
   r_   r   r   H  s   


zSqliteDict.updatec                 C   ry   r   rz   r_   r
   r
   r   __iter__Y  rg   zSqliteDict.__iter__c                 C   s@   | j dkr	tdd| j }| j  | j| | j  d S )Nr4   z&Refusing to clear read-only SqliteDictzDELETE FROM "%s";)rD   rC   rK   rR   rU   rT   )rW   	CLEAR_ALLr
   r
   r   rV   \  s   


zSqliteDict.clearc                 C   sf   t j| std|  d}t| }||}| }W d   n1 s'w   Y  dd |D S )z5get the names of the tables in an sqlite db as a listzfile %s does not existz1SELECT name FROM sqlite_master WHERE type="table"Nc                 S   s   g | ]}|d  qS )r   r
   )r   namer
   r
   r   r   p  s    z-SqliteDict.get_tablenames.<locals>.<listcomp>)r@   rE   isfileIOErrorr$   connectrT   fetchall)rI   GET_TABLENAMESrR   cursorresr
   r
   r   rS   f  s   

zSqliteDict.get_tablenamesc                 C   s   | j dur| j | dS dS )z
        Persist all data to disk.

        When `blocking` is False, the commit command is queued, but the data is
        not guaranteed persisted (default implication when autocommit=True).
        N)rR   rU   rW   blockingr
   r
   r   rU   r  s   
zSqliteDict.commitc                 C   s   |r	t d|   t| dr*| jd ur*| jjr |s | jjdd | jj|d d | _| jrAz	t	| j
 W d S  ty@   Y d S w d S )Nz
closing %srR   T)r   )force)rO   rP   r`   rR   rL   rU   rA   r=   r@   rG   rI   	Exception)rW   do_logr   r
   r
   r   rA   }  s   zSqliteDict.closec              	   C   s   | j dkr	td|   | jdkrdS td| j  ztj| jr-t	| j W dS W dS  t
tfyC   td| j  Y dS w )z3Delete the underlying database file. Use with care.r4   z*Refusing to terminate read-only SqliteDictz:memory:Nzdeleting %szfailed to delete %s)rD   rC   rA   rI   rO   infor@   rE   r   rG   OSErrorr   	exceptionr_   r
   r
   r   	terminate  s   

zSqliteDict.terminatec                 C   s*   z
| j ddd W d S  ty   Y d S w )NFT)r   r   )rA   r   r_   r
   r
   r   __del__  s
   zSqliteDict.__del__)r
   T)TF)#__name__
__module____qualname__rB   r)   r,   r2   r]   rQ   ra   rc   rd   rf   rl   ro   rs   rv   rx   r{   r|   r}   r   r   r   r   r   r   rV   staticmethodrS   rU   syncrA   r   r   r
   r
   r
   r   r      sD    
V
	



	
r   c                       st   e Zd ZdZd fdd	Zdd Zdd Zd	d
 ZdddZdd Z	dddZ
dddZdddZdddZ  ZS )r^   z
    Wrap sqlite connection in a way that allows concurrent requests from multiple threads.

    This is done by internally queueing the requests and processing them sequentially
    in a separate thread (in the same order they arrived).

    Tc                    sf   t t|   || _|| _|| _t | _d| _|| _	t
d| _t | _| j  d | _|   d S )NTzsqlitedict.SqliteMultithread)superr^   r]   rI   rL   rM   r   reqsdaemonrN   logging	getLoggerlog	threadingLock_lockacquirer   start)rW   rI   rL   rM   rY   	__class__r
   r   r]     s   

zSqliteMultithread.__init__c                 C   s   z| j rtj| jddd}ntj| jdd}W n ty-   | jd| j  t | _ w z|	d| j
  t|_| }|  |	d W ||fS  ty^   | jd t | _ w )	zConnect to the underlying database.

        Raises an exception on failure.  Returns the connection and cursor on success.
        NF)isolation_levelcheck_same_thread)r   z0Failed to initialize connection for filename: %szPRAGMA journal_mode = %szPRAGMA synchronous=OFFz$Failed to execute PRAGMA statements.)rL   r$   r   rI   r   r   r   sysrb   rT   rM   re   text_factoryr   rU   )rW   rR   r   r
   r
   r   _connect  s,   

zSqliteMultithread._connectc                 C   s  z|   \}}W | j  n| j  w d }	 | j \}}}}|tkr-|s,J d|fn|tkr;|  t|t	 nz|
|| W n} ty   | j t  | _\}}}	W d    n1 sbw   Y  t }
| jd t|
D ]}| j| qv| jd t||D ]}| j| q| jd | jr| jd t|D ]}| j| q| jd n| jd Y nw |r|D ]}t||tkr nqt|t	 | jr|  q| jd| |  t|t	 d S )	NTz--close-- without return queuezInner exception: zOuter stack:z)Exception will be re-raised at next call.zxUnable to show the outer stack. Pass outer_stack=True when initializing the SqliteDict instance to show the outer stack.zreceived: %s, send: --no more--)r   r   releaser   get_REQUEST_CLOSE_REQUEST_COMMITrU   r   _RESPONSE_NO_MORErT   r   r   rb   r   	tracebackextract_stackr   errorformat_listformat_exception_onlyrN   r   rL   rP   rA   )rW   rR   r   res_refreqargrY   e_typee_valuee_tbinner_stackr   recr
   r
   r   run  s`   %
FzSqliteMultithread.runc                 C   sl   | j ) | jr$| j\}}}d| _| jd t||| W d   dS W d   dS 1 s/w   Y  dS )a  
        Check for and raise exception for any previous sqlite query.

        For the `execute*` family of method calls, such calls are non-blocking and any
        exception raised in the thread cannot be handled by the calling Thread (usually
        MainThread).  This method is called on `close`, and prior to any subsequent
        calls to the `execute*` methods to check for and raise an exception in a
        previous call to the MainThread.
        NziAn exception occurred from a previous statement, view the logging namespace "sqlitedict" for outer stack.)r   r   r   r   r   )rW   r   r   r   r
   r
   r   check_raise_errorC  s   
	"z#SqliteMultithread.check_raise_errorNc                 C   sR   |    d}| jrt dd }d}|rt|}| j||p"t ||f dS )a  
        `execute` calls are non-blocking: just queue up the request and return immediately.

        :param req: The request (an SQL command)
        :param arg: Arguments to the SQL command
        :param res: A queue in which to place responses as they become available
        N)	r   rN   r   r   weakrefrefr   r   tuple)rW   r   r   r   stackr   r
   r
   r   rT   `  s   
zSqliteMultithread.executec                 C   s"   |D ]}|  || q|   d S r   )rT   r   )rW   r   r}   r   r
   r
   r   r   }  s   zSqliteMultithread.executemanyc                 c   s<    t  }| ||| 	 | }|   |tkrdS |V  q)aR  
        Unlike sqlite's native select, this select doesn't handle iteration efficiently.

        The result of `select` starts filling up with values as soon as the
        request is dequeued, and although you can iterate over the result normally
        (`for res in self.select(): ...`), the entire result will be in memory.
        TN)r   rT   r   r   r   )rW   r   r   r   r   r
   r
   r   rq     s   zSqliteMultithread.selectc                 C   s,   zt t| ||W S  ty   Y dS w )zOReturn only the first row of the SELECT, or None if there are no matching rows.N)nextiterrq   StopIteration)rW   r   r   r
   r
   r   ri     s
   zSqliteMultithread.select_onec                 C   s    |r	|  t d S | t d S r   )ri   r   rT   r   r
   r
   r   rU     s   zSqliteMultithread.commitFc                 C   s:   |r| j td tt d f d S | t |   d S r   )r   r   r   r   r   r   ri   join)rW   r   r
   r
   r   rA     s    
zSqliteMultithread.closer   )NNr   )F)r   r   r   __doc__r]   r   r   r   rT   r   rq   ri   rU   rA   __classcell__r
   r
   r   r   r^     s    V



r^   __main__r   )/r   r$   r@   r   r>   r   r   r   base64r   r   r   __version__r   cPickler   r   r   r&   ImportErrorpicklecollectionsr   	DictClassr   r   r   r   r   rO   r   r   r   r   r   r   r   r!   r)   r,   r0   r1   r2   r   Threadr^   printr
   r
   r
   r   <module>   s`   


    