
    >|hyi                         d Z ddlZddlmZ ddlmZ  G d de      Z G d d	e      ZeZ		 	 dd
Z
 G d d      Z G d d      Zy)a  SteadyDB - hardened DB-API 2 connections.

Implements steady connections to a database based on an
arbitrary DB-API 2 compliant database interface module.

The connections are transparently reopened when they are
closed or the database connection has been lost or when
they are used more often than an optional usage limit.
Database cursors are transparently reopened as well when
the execution of a database operation cannot be performed
due to a lost connection.  Only if the connection is lost
after the execution, when rows are already fetched from the
database, this will give an error and the cursor will not
be reopened automatically, because there is no reliable way
to recover the state of the cursor in such a situation.
Connections which have been marked as being in a transaction
with a begin() call will not be silently replaced either.

A typical situation where database connections are lost
is when the database server or an intervening firewall is
shutdown and restarted for maintenance reasons.  In such a
case, all database connections would become unusable, even
though the database service may be already available again.

The "hardened" connections provided by this module will
make the database connections immediately available again.

This approach results in a steady database connection that
can be used by PooledDB or PersistentDB to create pooled or
persistent connections to a database in a threaded environment
such as the application server of "Webware for Python."
Note, however, that the connections themselves may not be
thread-safe (depending on the used DB-API module).

For the Python DB-API 2 specification, see:
    https://www.python.org/dev/peps/pep-0249/
For information on Webware for Python, see:
    https://webwareforpython.github.io/w4py/

Usage:

You can use the connection constructor connect() in the same
way as you would use the connection constructor of a DB-API 2
module if you specify the DB-API 2 module to be used as the
first parameter, or alternatively you can specify an arbitrary
constructor function returning new DB-API 2 compliant connection
objects as the first parameter.  Passing just a function allows
implementing failover mechanisms and load balancing strategies.

You may also specify a usage limit as the second parameter
(set it to None if you prefer unlimited usage), an optional
list of commands that may serve to prepare the session as a
third parameter, the exception classes for which the failover
mechanism shall be applied, and you can specify whether is is
allowed to close the connection (by default this is true).
When the connection to the database is lost or has been used
too often, it will be transparently reset in most situations,
without further notice.

    import pgdb  # import used DB-API 2 module
    from dbutils.steady_db import connect
    db = connect(pgdb, 10000, ["set datestyle to german"],
        host=..., database=..., user=..., ...)
    ...
    cursor = db.cursor()
    ...
    cursor.execute('select ...')
    result = cursor.fetchall()
    ...
    cursor.close()
    ...
    db.close()


Ideas for improvement:

* Alternatively to the maximum number of uses,
  implement a maximum time to live for connections.
* Optionally log usage and loss of connection.


Copyright, credits and license:

* Contributed as supplement for Webware for Python and PyGreSQL
  by Christoph Zwerschke in September 2005
* Allowing creator functions as first parameter as in SQLAlchemy
  suggested by Ezio Vernacotola in December 2006

Licensed under the MIT license.
    N)suppress   )__version__c                       e Zd ZdZy)SteadyDBErrorzGeneral SteadyDB error.N__name__
__module____qualname____doc__     P/var/www/html/test/engine/venv/lib/python3.12/site-packages/dbutils/steady_db.pyr   r   b   s    !r   r   c                       e Zd ZdZy)InvalidCursorErrorzDatabase cursor is invalid.Nr   r   r   r   r   r   f   s    %r   r   c                 *    t        | |||||g|i |S )a  Create a "tough" connection.

    A hardened version of the connection function of a DB-API 2 module.

    creator: either an arbitrary function returning new DB-API 2 compliant
        connection objects or a DB-API 2 compliant database module
    maxusage: maximum usage limit for the underlying DB-API 2 connection
        (number of database operations, 0 or None means unlimited usage)
        callproc(), execute() and executemany() count as one operation.
        When the limit is reached, the connection is automatically reset.
    setsession: an optional list of SQL commands that may serve to prepare
        the session, e.g. ["set datestyle to german", "set time zone mez"]
    failures: an optional exception class or a tuple of exception classes
        for which the failover mechanism shall be applied, if the default
        (OperationalError, InternalError, Interface) is not adequate
        for the used database module
    ping: determines when the connection should be checked with ping()
        (0 = None = never, 1 = default = when _ping_check() is called,
        2 = whenever a cursor is created, 4 = when a query is executed,
        7 = always, and all other bit combinations of these values)
    closeable: if this is set to false, then closing the connection will
        be silently ignored, but by default the connection can be closed
    args, kwargs: the parameters that shall be passed to the creator
        function or the connection constructor of the DB-API 2 module
    )SteadyDBConnection)creatormaxusage
setsessionfailuresping	closeableargskwargss           r   connectr   n   s/    8 :$	4$(4,24 4r   c                       e Zd ZdZeZ	 	 ddZd Zd Zd Z	ddZ
d Zd	 Zdd
ZddZd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zy)r   z+A hardened version of DB-API 2 connections.Nc                 >   d| _         d| _        	 |j                  | _        	 |j                  j                  r|j                  | _        	 |j                  | _        t        | j                        st        |d      |d}t        |t               st        d      || _        || _        |+t        |t&              st)        |t*              st        d      || _        t        |t               r|nd| _        || _        ||c| _        | _        | j7                  | j9                                y# t        $ r
 || _        Y w xY w# t        $ r || _        	 |j                  | _        nn# t        $ rb 	 t        j                  |j                     | _        | j
                  j                  |k7  rt        n# t        t        f$ r
 d| _        Y nw xY wY nw xY wY w xY w# t        $ r7 	 | j
                  j                  | _        n# t        $ r
 d| _        Y nw xY wY w xY w)z%Create a "tough" DB-API 2 connection.NTz is not a connection provider.r   z$'maxusage' must be an integer value.z)'failures' must be a tuple of exceptions.)_con_closedr   _creatordbapi_dbapiAttributeErrorsysmodulesr
   KeyErrorthreadsafety_threadsafetycallable	TypeError
isinstanceint	_maxusage_setsession_sqltuple
issubclass	Exception	_failures_ping
_closeable_args_kwargs_store_create)	selfr   r   r   r   r   r   r   r   s	            r   __init__zSteadyDBConnection.__init__   s   
 		'#OODM&==((")--DK	*!(!5!5D &wk)GHIIH(C(BCC!)
%)!)3Hi)HGHH!'c2T
##' 
DLDLLN#I " &%& 	'#DM'%mm! ''"%++g.@.@"ADK{{**g5,, 6&1 '"&DK''	'  	**%)[[%=%="! *%)"*	*s   E 'D. 
G .E>E  EE GE'&G'	G1AF32G3G	GGGGGGG	H&HHHHHHHc                     | S )z4Enter the runtime context for the connection object.r   r:   s    r   	__enter__zSteadyDBConnection.__enter__       r   c                 d    |d   |d   |d   | j                          y| j                          y)zExit the runtime context for the connection object.

        This does not close the connection, but it ends a transaction.
        r   Nr      )commitrollbackr:   excs     r   __exit__zSteadyDBConnection.__exit__   s/    
 q6>c!fnQKKMMMOr   c                 V    | j                   | j                  i | j                  }	 	 | j                  j                  | j                   k7  rt
        	 | j                  	 | j                  j                  | _        | j                   G	 | j                  j                  | j                  j"                  | j                  j$                  f| _        t'        | j                   t(              r| j                   d   | _        n| j                   | _        | j-                  |       |S # t
        $ rA 	 |j                  }n# t
        $ r d}Y nw xY w|ru	 t        j                  |   | _        t        | j                  j                        st
        	 n# t
        t        f$ r Y nw xY w|j                  d      }|dk  rdn|d| }|ru	 |j                  j                  }n# t
        $ r d}Y nw xY w|ru	 t        j                  |   | _        t        | j                  j                        st
        	 n<# t
        t        f$ r Y nw xY w|j                  d      }|dk  rdn|d| }|rud| _        Y w xY w# t
        $ r: t        t
              5  |j                  | _        ddd       n# 1 sw Y   nxY wY 6w xY w# t
        $ r 	 | j                   j                  | j                   j"                  | j                   j$                  f| _        nV# t
        $ rJ 	 |j                  |j"                  |j$                  f| _        n# t
        $ r}t        d      |d}~ww xY wY nw xY wY w xY w# t.        $ r<}t        t.              5  |j1                          ddd       |# 1 sw Y   |xY wd}~ww xY w)z3Create a new connection using the creator function.N.r   zNCould not determine failure exceptions (please set failures or creator.dbapi).)r!   r6   r7   r#   r   r$   r
   r%   r&   r*   r'   rfindOperationalErrorr)   r(   r   r3   InterfaceErrorInternalErrorr,   r0   _failure_setsessionr2   close)r:   conmodierrors        r   r9   zSteadyDBConnection._create   sw   dmmTZZ84<<8N	%+;;&&$--7(( 8J !!)>)-)A)AD& ~~%)442211&3DN* $..%0 $q 1 $S! 
W " "+..C% C&)kk#&6'(;(;<"00  =
  +H5  		#A"#a%$S!WC #!22==) #"#"*-++c*:DK#+DKK,?,?#@&4 4 $A
 " !/9 ! !  IIcN&'!edRa  '+E"+L & >!.1 >-0-=-=*> > >> & )) MM:: MM88 MM77*9 * 
)	) # 4 4 # 2 2 # 1 1.3DN  . )"0!K# $)))	 +
)),  	 )$ 		KK	s  )D! M#  I/ ;M# AJ5 AM# !
I,,D98I,9EI,EI,=FI,FI,F"I,GI,G'$I,&G''I,-=H,+I,,H>;I,=H>>"I,!I,(M# +I,,M# /J2J"	J2"J+	'J2.M# 1J22M# 5	M ?ALM 	M(L98M9	MMMMM MM M# M  M# #	N(,N#;N
N#N 	N##N(c                     || j                   }| j                  rC|j                         }| j                  D ]  }|j                  |        |j	                          yy)z1Execute the SQL commands for session preparation.N)r   r/   cursorexecuterO   )r:   rP   rU   sqls       r   rN   zSteadyDBConnection._setsession)  sS    ;))CZZ\F++ $s#$LLN	  r   c                 <    || _         d| _        d| _        d| _        y)z/Store a database connection for subsequent use.Fr   N)r   _transactionr    _usage)r:   rP   s     r   r8   zSteadyDBConnection._store3  s    	!r   c                     | j                   sAt        t              5  | j                  j	                          ddd       d| _        d| _         yy# 1 sw Y   xY w)zClose the tough connection.

        You can always close a tough connection with this method,
        and it will not complain if you close it more than once.
        NFT)r    r   r2   r   rO   rY   r=   s    r   _closezSteadyDBConnection._close:  sJ     ||)$ "		!" %DDL	 " "s   AAc                     | j                   s8|s| j                  r)t        t              5  | j	                          ddd       yyy# 1 sw Y   yxY w)zfReset a tough connection.

        Rollback if forced or the connection was in a transaction.
        N)r    rY   r   r2   rC   )r:   forces     r   _resetzSteadyDBConnection._resetF  sG    
 ||$*;*;)$      +<|   s   AAc                    || j                   z  rl	 	 | j                  j                  d      }|d}|rd}	 |rB| j                  s6	 | j                         }| j                          | j                  |       d}|S |S y# t        $ r | j                  j                         }Y uw xY w# t        t
        t        t        f$ r d| _         d}d}Y t        $ r d}Y w xY w# t        $ r Y |S w xY w)a  Check whether the connection is still alive using ping().

        If the underlying connection is not active and the ping
        parameter is set accordingly, the connection will be recreated
        unless the connection is currently inside a transaction.
        FNTr   )r4   r   r   r+   r$   
IndexError
ValueErrorr2   rY   r9   r\   r8   )r:   r   	reconnectaliverP   s        r   _ping_checkzSteadyDBConnection._ping_checkO  s     $**&- IINN51E = E %I!2!2!,,.C KKMKK$ EL5L/ ! - IINN,E-"J	:F "
!	  ! 
 LsA   A= C =#B# B& "B##B& &$C
CC	C)(C)c                 H    | j                   t        d      | j                   S )z8Return the underlying DB-API 2 module of the connection.z?Could not determine DB-API 2 module (please set creator.dbapi).)r#   r$   r=   s    r   r"   zSteadyDBConnection.dbapis  s*    ;; /0 0 {{r   c                 b    | j                   | j                  t        d      y| j                   S )z1Return the thread safety level of the connection.zTCould not determine threadsafety (please set creator.dbapi or creator.threadsafety).r   )r)   r#   r$   r=   s    r   r(   zSteadyDBConnection.threadsafety{  s>    %{{"$KL L !!!r   c                 x    | j                   r| j                          y| j                  r| j                          yy)a^  Close the tough connection.

        You are allowed to close a tough connection by default,
        and it will not complain if you close it more than once.

        You can disallow closing connections by setting
        the closeable parameter to something false.  In this case,
        closing tough connections will be silently ignored.
        N)r5   r\   rY   r_   r=   s    r   rO   zSteadyDBConnection.close  s+     ??KKMKKM r   c                 n    d| _         	 | j                  j                  } ||i | y# t        $ r Y yw xY w)aH  Indicate the beginning of a transaction.

        During a transaction, connections won't be transparently
        replaced, and all errors will be raised to the application.

        If the underlying driver supports this method, it will be called
        with the given parameters (e.g. for distributed transactions).
        TN)rY   r   beginr$   )r:   r   r   rj   s       r   rj   zSteadyDBConnection.begin  sA     !	#IIOOE 4"6"  		s   ( 	44c                     d| _         	 | j                  j                          y# | j                  $ rI}	 | j	                         }| j                          | j                  |       |# t        $ r Y |w xY wd}~ww xY w)zCommit any pending transaction.FN)rY   r   rB   r3   r9   r\   r8   r2   r:   rS   rP   s      r   rB   zSteadyDBConnection.commit  sy    !
	II~~ 	!lln C K   K	2   $ A<A'#A7'	A40A73A44A77A<c                     d| _         	 | j                  j                          y# | j                  $ rI}	 | j	                         }| j                          | j                  |       |# t        $ r Y |w xY wd}~ww xY w)zRollback pending transaction.FN)rY   r   rC   r3   r9   r\   r8   r2   rl   s      r   rC   zSteadyDBConnection.rollback  sy    !
	II ~~ 	!lln C K   K	rm   c                 l    d| _         	 | j                  j                  } |        y# t        $ r Y yw xY w)zvCancel a long-running transaction.

        If the underlying driver supports this method, it will be called.
        FN)rY   r   cancelr$   )r:   rp   s     r   rp   zSteadyDBConnection.cancel  s;    
 "	YY%%F H  		s   ' 	33c                 :     | j                   j                  |i |S )zPing connection.)r   r   r:   r   r   s      r   r   zSteadyDBConnection.ping  s    tyy~~t.v..r   c                 n   | j                   }|s| j                  d       	 | j                  r'| j                  | j                  k\  r|s| j                   | j
                  j                  |i |}|S # | j                  $ r}	 | j                         }	  |j                  |i |}| j                          | j                  |       |r||cY d}~S # t        $ r Y nw xY wt        t              5  |j                          ddd       n# 1 sw Y   nxY w# t        $ r Y nw xY w|rd| _         |d}~ww xY w)z]Create a "tough" cursor.

        This is a hardened version of the method cursor().
        rA   NF)rY   re   r.   rZ   rM   r   rU   r3   r9   r\   r8   r2   r   rO   )r:   r   r   transactionrU   rS   rP   s          r   _cursorzSteadyDBConnection._cursor  s/    ''Q	4;;$..#@'mm#%TYY%%t6v6F, + ~~ 	 lln	"'SZZ88F KKMKK$"#!M !  i(  IIK        $)!K)	sr   AA2 2D4DC%&D/D4	CD/CD//D	 	D/	D	D/	D!D/ D!!D//D4c                      t        | g|i |S )z0Return a new Cursor Object using the connection.)SteadyDBCursorrr   s      r   rU   zSteadyDBConnection.cursor  s    d4T4V44r   c                 4    	 | j                          y#  Y yxY w)zDelete the steady connection.N)r\   r=   s    r   __del__zSteadyDBConnection.__del__  s    	KKM	    NNNr   TN)F)r   T)r	   r
   r   r   r   versionr;   r>   rF   r9   rN   r8   r\   r_   re   r"   r(   rO   rj   rB   rC   rp   r   ru   rU   ry   r   r   r   r   r      s{    5G 6:-11$fRh
  "H"#"/%N5r   r   c                   \    e Zd ZdZd Zd Zd Zd Zd ZddZ	d	 Z
dd
Zd Zd Zd Zd Zy)rw   z'A hardened version of DB-API 2 cursors.c                     d| _         d| _        || _        ||c| _        | _        | j                          	  |j                   |i || _         d| _        y# t        $ r}t        |d      |d}~ww xY w)z!Create a "tough" DB-API 2 cursor.NTz is not a SteadyDBConnection.F)ru   r    r   r6   r7   _clearsizesr$   r+   )r:   rP   r   r   rS   s        r   r;   zSteadyDBCursor.__init__  s     	#' 
DL	P&3;;77DL   	Psg%BCD%O	Ps   A 	A2A--A2c                     | S )z0Enter the runtime context for the cursor object.r   r=   s    r   r>   zSteadyDBCursor.__enter__  r?   r   c                 $    | j                          y)z/Exit the runtime context for the cursor object.NrO   rD   s     r   rF   zSteadyDBCursor.__exit__  s    

r   c                 |    | j                   }	 t        |      S # t        $ r t        |j                  d      cY S w xY w)z1Make cursor compatible to the iteration protocol.N)ru   iterr+   fetchone)r:   rU   s     r   __iter__zSteadyDBCursor.__iter__"  s:    	/< 	/..	/s   
 ;;c                     || _         y)z6Store input sizes in case cursor needs to be reopened.N)_inputsizes)r:   sizess     r   setinputsizeszSteadyDBCursor.setinputsizes*  s
     r   Nc                 "    || j                   |<   y)z7Store output sizes in case cursor needs to be reopened.N)_outputsizes)r:   sizecolumns      r   setoutputsizezSteadyDBCursor.setoutputsize.  s    $(&!r   c                      g | _         i | _        y)z$Clear stored input and output sizes.N)r   r   r=   s    r   r   zSteadyDBCursor._clearsizes2  s    r   c                     || j                   }| j                  r|j                  | j                         | j                  j	                         D ]+  \  }}||j                  |       |j                  ||       - y)z7Set stored input and output sizes for cursor execution.N)ru   r   r   r   itemsr   )r:   rU   r   r   s       r   	_setsizeszSteadyDBCursor._setsizes7  sr    >\\F  !1!12 --335 	3LFD~$$T*$$T62		3r   c                     | j                   s:t        t              5  | j                  j	                          ddd       d| _         yy# 1 sw Y   xY w)z^Close the tough cursor.

        It will not complain if you close it more than once.
        NT)r    r   r2   ru   rO   r=   s    r   rO   zSteadyDBCursor.closeC  sD    
 ||)$ %""$%DL % %s   AAc                       fd}|S )z4Return a "tough" version of the given cursor method.c                  4   j                  d      }j                  }|j                  }|s|j                  d       	 |j                  r'|j
                  |j                  k\  r|s|j                  |rj                          t        j                        } || i |}|rj                          |xj
                  dz  c_        |S # |j                  $ r}|s	  |j                  j                  i j                  }	 |rj                  |       t        |      } || i |}|rj                          j                          |_	        |xj
                  dz  c_        |cY d }~S # t        $ r Y nw xY wt!        t              5  |j                          d d d        n# 1 sw Y   nxY w# t        $ r Y nw xY w	 |j#                         }		  |	j$                  j                  i j                  }|r:j                          |j'                          |j)                  |	       |_	        |d }
	 |rj                  |       t        |      } || i |}|rj                          d}n.# |j*                  $ r d}|}
Y nt        $ r}d}|}
Y d }~nd }~ww xY w|rXj                          |j'                          |j)                  |	       |_	        |xj
                  dz  c_        |
r|
cY d }~S t!        t              5  |j                          d d d        n# 1 sw Y   nxY w# t        $ r Y nw xY wt!        t              5  |	j                          d d d        n# 1 sw Y   nxY w# t        $ r Y nw xY w|rd_        d }~ww xY w)NrV      r   TF)
startswithr   rY   re   r.   rZ   rM   r   getattrru   r   r3   r6   r7   rO   r2   r   r9   rU   r\   r8   	__class__)r   r   rV   rP   rt   methodresultrS   cursor2con2error2method2use2namer:   s                r   tough_methodz6SteadyDBCursor._get_tough_method.<locals>.tough_methodO  sT   ooi0G))C**K"VMMcjjCMM&A +,,&NN$ t400$$&V 

a
W == H","-#++!ZZ#9+/<<#9
*& $w 7%,Wd%;F%+T%<V%<F& $ 0 0 2 !JJL+2DLJJ!OJ#)M  ) ! ! &i0 ,#MMO, , ,# % &-%;;=D&,"-$++!ZZ#9+/<<#9
 ' JJLJJLJJt,+2DL"'K!%(& $w 7&-gt&<G%,d%=f%=F& $ 0 0 2 $(D  % +#(D%*F( +#'D%*F+
   JJLJJLJJt,+2DLJJ!OJ%&,#)M%i0 ,#MMO, , ,E % H "), %

% % %U ! X (-D%QHs-  A5C NN&F59E1>-N+N1	E=:N<E==NF) 	N)F2	.N5	G>N GNM8&L4<>N;9I74N7J"
NJ"JNJ""AN9N?NL(	N(L1	-N4	M =N?M  NM,#	N,M5	1N8	NNNNNr   )r:   r   r   s   `` r   _get_tough_methodz SteadyDBCursor._get_tough_methodM  s    \	z r   c                     | j                   r8|j                  d      r| j                  |      S t        | j                   |      S t        )z4Inherit methods and attributes of underlying cursor.)rV   call)ru   r   r   r   r   )r:   r   s     r   __getattr__zSteadyDBCursor.__getattr__  s>    <<23--d334<<..  r   c                 4    	 | j                          y#  Y yxY w)zDelete the steady cursor.Nr   r=   s    r   ry   zSteadyDBCursor.__del__  s    	JJL	rz   r|   )r	   r
   r   r   r;   r>   rF   r   r   r   r   r   rO   r   r   ry   r   r   r   rw   rw     sC    1/!)

3 _B!r   rw   r{   )r   r%   
contextlibr    r   r2   r   r   InvalidCursorr   r   rw   r   r   r   <module>r      s]   Yv   "I "& &
 # ,0)-4Bv vru ur   