U
    b+d*                  
   @   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mZ d dl	m
Z
 d dlmZmZmZ d dlmZ d dlmZmZ d dlmZmZmZ d dlmZ d d	lmZ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+ d dl,m-Z- d dl.m/Z/m0Z0m1Z1m2Z2 zd dl3Z3dZ4W n e5k
rL   dZ4Y nX e6ej7iZ8e4re9e3drde8e3j:< de8e3j;< n
de8e3j<< e=e8> Z?e0rd dl@Z@dZAdZBdZCdZDdZEeF ZGdZHdZIdZJdZKeedZLG dd dZMG dd dZNG dd  d ZOG d!d" d"eNZPG d#d$ d$eNZQeNeRd%< e0r6eQZSnePZSG d&d' d'ZTG d(d) d)ZUG d*d+ d+ZVG d,d- d-eVZWG d.d/ d/eVZXd0ZYd1d2 ZZe[e\e\eZeZe]e[e[eZd3	Z^d4d5 Z_G d6d7 d7Z`G d8d9 d9e`ZadS ):    N)SEEK_END)chain)EmptyFull	LifoQueue)time)OptionalUnion)parse_qsunquoteurlparse)	NoBackoff)CredentialProvider"UsernamePasswordCredentialProvider)AuthenticationError$AuthenticationWrongNumberOfArgsErrorBusyLoadingErrorChildDeadlockedErrorConnectionError	DataErrorExecAbortErrorInvalidResponseModuleErrorNoPermissionErrorNoScriptErrorReadOnlyError
RedisErrorResponseErrorTimeoutError)Retry)CRYPTOGRAPHY_AVAILABLEHIREDIS_AVAILABLEHIREDIS_PACK_AVAILABLEstr_if_bytesTFSSLWantReadError      *   $s   
    zConnection closed by server.z:Error loading the extension. Please check the server logs.z5Error unloading module: no such module with that namez/Error unloading module: operation not possible.z[Error unloading module: the module exports one or more module-side data types, can't unload)zxAUTH <password> called without any password configured for the default user. Are you sure your configuration is correct?z(Client sent AUTH, but no password is setc                   @   s*   e Zd ZdZdd Zdd Zd
ddZd	S )Encoderz=Encode strings to bytes-like and decode bytes-like to stringsc                 C   s   || _ || _|| _d S Nencodingencoding_errorsdecode_responses)selfr,   r-   r.    r0   4/tmp/pip-unpacked-wheel-cdsyf3nb/redis/connection.py__init__^   s    zEncoder.__init__c                 C   s   t |ttfr|S t |tr&tdn@t |ttfrBt| }n$t |t	sft
|j}td| dt |t	r|| j| j}|S )z=Return a bytestring or bytes-like representation of the valuezNInvalid input of type: 'bool'. Convert to a bytes, string, int or float first.zInvalid input of type: 'z2'. Convert to a bytes, string, int or float first.)
isinstancebytes
memoryviewboolr   intfloatreprencodestrtype__name__r,   r-   )r/   valuetypenamer0   r0   r1   r:   c   s     




zEncoder.encodeFc                 C   s:   | j s
|r6t|tr| }t|tr6|| j| j}|S )z:Return a unicode string from the bytes-like representation)r.   r3   r5   tobytesr4   decoder,   r-   )r/   r>   forcer0   r0   r1   rA   z   s    


zEncoder.decodeN)F)r=   
__module____qualname____doc__r2   r:   rA   r0   r0   r0   r1   r)   [   s   r)   c                   @   sN   e Zd Zdedededeeeeee	ee
eieeeeeeeedZdd ZdS )	
BaseParserzmax number of clients reachedzinvalid passwordz,wrong number of arguments for 'auth' commandz,wrong number of arguments for 'AUTH' command)ZERRZ	WRONGPASSZ	EXECABORTZLOADINGZNOSCRIPTZREADONLYZNOAUTHZNOPERMc                 C   s\   | dd }|| jkrT|t|d d }| j| }t|trL||t}||S t|S )zParse an error response r      N)splitEXCEPTION_CLASSESlenr3   dictgetr   )r/   responseZ
error_codeZexception_classr0   r0   r1   parse_error   s    


zBaseParser.parse_errorN)r=   rC   rD   r   r   r   MODULE_LOAD_ERRORr   MODULE_EXPORTS_DATA_TYPES_ERRORNO_SUCH_MODULE_ERROR MODULE_UNLOAD_NOT_POSSIBLE_ERRORNO_AUTH_SET_ERRORr   r   r   r   r   rJ   rO   r0   r0   r0   r1   rF      s8         rF   c                   @   s   e Zd ZejeedddZedddZdedfe	e e
eef e	e ed	d
dZeedddZeedddZedddZedddZeddddZddddZddddZdS )SocketBuffer)socketsocket_read_sizesocket_timeoutc                 C   s    || _ || _|| _t | _d S r*   )_sockrW   rX   ioBytesIO_buffer)r/   rV   rW   rX   r0   r0   r1   r2      s    zSocketBuffer.__init__returnc                 C   s,   | j  }| j dt}| j | || S )z3
        Remaining unread length of buffer
        r   )r\   tellseekr   )r/   posendr0   r0   r1   unread_bytes   s    
zSocketBuffer.unread_bytesNT)lengthtimeoutraise_on_timeoutr^   c              
   C   sR  | j }| j}d}|tk	}| j}| }	|dt |r@|| zz`| j 	|}
t
|
trnt|
dkrntt||
 t|
}||7 }|d k	r||krqDW W dS  tjk
r   |rtdY W jdS  tk
r* } zBt|jd}|s
|j|kr
W Y W *dS td|j W 5 d }~X Y nX W 5 ||	 |rL|| j X d S Nr   TzTimeout reading from socketFz!Error while reading from socket: )rY   rW   SENTINELr\   r_   r`   r   
settimeoutrX   recvr3   r4   rK   r   SERVER_CLOSED_CONNECTION_ERRORwriterV   re   r   NONBLOCKING_EXCEPTIONS#NONBLOCKING_EXCEPTION_ERROR_NUMBERSrM   	__class__errnoargs)r/   rd   re   rf   sockrW   markercustom_timeoutbufZcurrent_posdataZdata_lengthexallowedr0   r0   r1   _read_from_socket   s>    



&
zSocketBuffer._read_from_socket)re   r^   c                 C   s   t |  p| j|ddS )NFre   rf   )r6   rc   rz   r/   re   r0   r0   r1   can_read   s     zSocketBuffer.can_read)rd   r^   c                 C   sJ   |d }| j |}|t| }|r>| | || j |7 }|d d S )Nr%   )r\   readrK   rz   )r/   rd   rw   missingr0   r0   r1   r      s    
zSocketBuffer.readc                 C   s:   | j }| }|ts.|   || 7 }q|d d S )Nr~   )r\   readlineendswithSYM_CRLFrz   )r/   rv   rw   r0   r0   r1   r      s    
zSocketBuffer.readlinec                 C   s
   | j  S )z+
        Get current read position
        )r\   r_   r/   r0   r0   r1   get_pos  s    zSocketBuffer.get_pos)ra   r^   c                 C   s   | j | dS )zO
        Rewind the buffer to a specific position, to re-start reading
        N)r\   r`   )r/   ra   r0   r0   r1   rewind  s    zSocketBuffer.rewindc                 C   sX   |   }|dkrdS |dkr<| j }|| d |d|< | j| | jd dS )zH
        After a successful read, purge the read part of buffer
        r   N)rc   r\   	getbuffertruncater`   )r/   Zunreadviewr0   r0   r1   purge  s    
zSocketBuffer.purgec                 C   s4   z| j   W n tk
r"   Y nX d | _ d | _d S r*   )r\   close	ExceptionrY   r   r0   r0   r1   r   &  s    zSocketBuffer.close)r=   rC   rD   rV   r7   r8   r2   rc   ri   r   r	   objectr6   rz   r}   r4   r   r   r   r   r   r   r0   r0   r0   r1   rU      s*     
/
rU   c                   @   sL   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d ZdddZ	dddZ
dS )PythonParserzPlain Python parsing classc                 C   s   || _ d | _d | _d | _d S r*   )rW   encoderrY   r\   r/   rW   r0   r0   r1   r2   7  s    zPythonParser.__init__c                 C   s&   z|    W n tk
r    Y nX d S r*   on_disconnectr   r   r0   r0   r1   __del__=  s    zPythonParser.__del__c                 C   s(   |j | _ t| j | j|j| _|j| _dS )zCalled when the socket connectsN)rY   rU   rW   rX   r\   r   r/   
connectionr0   r0   r1   
on_connectC  s      zPythonParser.on_connectc                 C   s*   d| _ | jdk	r | j  d| _d| _dS )z"Called when the socket disconnectsN)rY   r\   r   r   r   r0   r0   r1   r   K  s
    

zPythonParser.on_disconnectc                 C   s   | j o| j |S r*   )r\   r}   r|   r0   r0   r1   r}   S  s    zPythonParser.can_readFc                 C   s`   | j r| j  nd }z| j|d}W n( tk
rL   | j rF| j |  Y nX | j   |S d S )Ndisable_decoding)r\   r   _read_responseBaseExceptionr   r   )r/   r   ra   resultr0   r0   r1   read_responseV  s    
zPythonParser.read_responsec                    s  j  }|stt|d d |dd   }}|dkrFtd||dkrx|jddd}|}t|trt||S |dkrnp|d	krt|}n^|d
krt|}|dkrd S j 	|}n4|dkrt|}|dkrd S  fddt
|D }t|tr dkrj|}|S )NrH   )   -   +   :r'   r&   zProtocol Error: r   utf-8replace)errorsr   r   r'   rh   r&   c                    s   g | ]}j  d qS )r   )r   ).0ir   r/   r0   r1   
<listcomp>  s   z/PythonParser._read_response.<locals>.<listcomp>F)r\   r   r   rl   r   rA   rO   r3   r7   r   ranger4   r   )r/   r   rawbyterN   errorrd   r0   r   r1   r   b  s>    



zPythonParser._read_responseN)F)F)r=   rC   rD   rE   r2   r   r   r   r}   r   r   r0   r0   r0   r1   r   4  s   
r   c                   @   sP   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d Ze	dfddZ
dddZdS )HiredisParserz*Parser class for connections using Hiredisc                 C   s    t std|| _t|| _d S )NzHiredis is not installed)r!   r   rW   	bytearrayr\   r   r0   r0   r1   r2     s    zHiredisParser.__init__c                 C   s&   z|    W n tk
r    Y nX d S r*   r   r   r0   r0   r1   r     s    zHiredisParser.__del__c                 K   sN   |j | _ |j| _t| j|jjd}|jjr6|jj|d< t	j
f || _d| _d S )N)ZprotocolErrorZ
replyErrorr   r,   F)rY   rX   _socket_timeoutr   rO   r   r-   r.   r,   hiredisReader_reader_next_response)r/   r   kwargsr0   r0   r1   r     s    zHiredisParser.on_connectc                 C   s   d | _ d | _d| _d S )NF)rY   r   r   r   r0   r0   r1   r     s    zHiredisParser.on_disconnectc                 C   s@   | j stt| jdkr<| j  | _| jdkr<| j|ddS dS )NFr{   T)r   r   rl   r   getsread_from_socketr|   r0   r0   r1   r}     s    

zHiredisParser.can_readTc              
   C   s   | j }|tk	}zzH|r || | j | j}|dkr>tt| j	| jd| W W dS  t
jk
r   |rvtdY W ddS  tk
r } z>t|jd}|s|j|krW Y W *dS td|j W 5 d }~X Y nX W 5 |r|| j X d S rg   )rY   ri   rj   r   	recv_intor\   r   rl   r   feedrV   re   r   rn   ro   rM   rp   rq   rr   )r/   re   rf   rs   ru   Zbufflenrx   ry   r0   r0   r1   r     s*    


&zHiredisParser.read_from_socketFc                 C   s   | j stt| jdk	r(| j}d| _|S |r:| j d}n
| j  }|dkrr|   |rf| j d}qD| j  }qDt|tr|n$t|tr|rt|d tr|d |S )NFr   )r   r   rl   r   r   r   r3   list)r/   r   rN   r0   r0   r1   r     s0    


zHiredisParser.read_responseN)F)r=   rC   rD   rE   r2   r   r   r   r}   ri   r   r   r0   r0   r0   r1   r     s   
r   DefaultParserc                   @   s   e Zd Zdd ZdS )HiredisRespSerializerc                 G   s   g }t |d tr4t|d   |dd  }n(d|d kr\t|d  |dd  }z|t| W n0 tk
r   t	
 \}}}t||Y nX |S 2Pack a series of arguments into the Redis protocolr   rH   N    )r3   r;   tupler:   rI   appendr   pack_command	TypeErrorsysexc_infor   with_traceback)r/   rr   output_r>   	tracebackr0   r0   r1   pack  s    "zHiredisRespSerializer.packN)r=   rC   rD   r   r0   r0   r0   r1   r     s   r   c                   @   s"   e Zd ZddddZdd ZdS )PythonRespSerializerNr]   c                 C   s   || _ || _d S r*   )_buffer_cutoffr:   )r/   buffer_cutoffr:   r0   r0   r1   r2     s    zPythonRespSerializer.__init__c              	   G   s  g }t |d tr4t|d   |dd  }n(d|d kr\t|d  |dd  }tttt| t	f}| j
}t| j|D ]|}t|}t||ks||kst |trt|tt| t	f}|| || t	}qt|tt| t	|t	f}q|| |S r   )r3   r;   r   r:   rI   	SYM_EMPTYjoinSYM_STARrK   r   r   mapr5   
SYM_DOLLARr   )r/   rr   r   Zbuffr   argZ
arg_lengthr0   r0   r1   r     s@    "





zPythonRespSerializer.pack)r=   rC   rD   r2   r   r0   r0   r0   r1   r     s   r   c                   @   s  e Zd ZdZddddddddddeddded	dddddddfee d
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d;d2d3Zd<d4d5Zd6d7 Zd8d9 ZdS )=
Connectionz4Manages TCP communication to and from a Redis server	localhosti  r   NFr   strict   credential_providerc                 C   s6  |s|r|dk	rt dt | _|| _t|| _|| _|| _|| _	|| _
|| _|| _|p\|| _|| _|pli | _|	| _|
| _|tkrg }|
r|t || _|s|r|dkrtt d| _nt|| _| j| ntt d| _|| _d| _|| _t|||| _ d| _!|| _"| #| g | _$d| _%| &|| _'dS )a2  
        Initialize a new Connection.
        To specify a retry policy for specific errors, first set
        `retry_on_error` to a list of the error/s to retry on, then set
        `retry` to a valid `Retry` object.
        To retry on TimeoutError, `retry_on_timeout` can also be set to `True`.
        N'username' and 'password' cannot be passed along with 'credential_provider'. Please provide only one of the following arguments: 
1. 'password' and (optional) 'username'
2. 'credential_provider'rH   r   p  )(r   osgetpidpidhostr7   portdbclient_namer   passwordusernamerX   socket_connect_timeoutsocket_keepalivesocket_keepalive_optionssocket_typeretry_on_timeoutri   r   r   retry_on_errorr   r   retrycopydeepcopyupdate_supported_errorshealth_check_intervalnext_health_checkredis_connect_funcr)   r   rY   _socket_read_size
set_parser_connect_callbacksr   _construct_command_packer_command_packer)r/   r   r   r   r   rX   r   r   r   r   r   r   r,   r-   r.   parser_classrW   r   r   r   r   r   r   command_packerr0   r0   r1   r2   O  sN    !





zConnection.__init__c                 C   s,   d dd |  D }| jj d| dS )N,c                 S   s   g | ]\}}| d | qS )=r0   )r   kvr0   r0   r1   r     s     z'Connection.__repr__.<locals>.<listcomp><>)r   repr_piecesrp   r=   )r/   Z	repr_argsr0   r0   r1   __repr__  s    zConnection.__repr__c                 C   s6   d| j fd| jfd| jfg}| jr2|d| jf |S )Nr   r   r   r   )r   r   r   r   r   r/   piecesr0   r0   r1   r     s    zConnection.repr_piecesc                 C   s&   z|    W n tk
r    Y nX d S r*   )
disconnectr   r   r0   r0   r1   r     s    zConnection.__del__c                 C   s*   |d k	r|S t rt S t| j| jjS d S r*   )r"   r   r   r   r   r:   )r/   packerr0   r0   r1   r     s
    z$Connection._construct_command_packerc                 C   s   | j t| d S r*   )r   r   weakref
WeakMethod)r/   callbackr0   r0   r1   register_connect_callback  s    z$Connection.register_connect_callbackc                 C   s
   g | _ d S r*   )r   r   r0   r0   r1   clear_connect_callbacks  s    z"Connection.clear_connect_callbacksc                 C   s   || j d| _dS )z
        Creates a new instance of parser_class with socket size:
        _socket_read_size and assigns it to the parser for the connection
        :param parser_class: The required parser class
        )rW   N)r   _parser)r/   r   r0   r0   r1   r     s    zConnection.set_parserc              
      s    j r
dS z" j fdd fdd}W nL tjk
rJ   tdY n0 tk
rx } zt |W 5 d}~X Y nX | _ z" j	dkr 
  n
 	  W n tk
r       Y nX  jD ]}| }|r|  qdS )z5Connects to the Redis server if not already connectedNc                      s      S r*   )_connectr0   r   r0   r1   <lambda>  r(   z$Connection.connect.<locals>.<lambda>c                    s
     | S r*   r   )r   r   r0   r1   r	    r(   zTimeout connecting to server)rY   r   call_with_retryrV   re   r   OSErrorr   _error_messager   r   r   r   r   )r/   rs   erefr  r0   r   r1   connect  s.    
 
 


zConnection.connectc                 C   s  d}t | j| j| jt jD ]}|\}}}}}d}zt  |||}|t jt jd | j	r|t j
t jd | j D ]\}	}
|t j|	|
 qv|| j || || j |W   S  tk
r } z|}|dk	r|  W 5 d}~X Y qX q|dk	r|tddS )zCreate a TCP socket connectionNrH   z)socket.getaddrinfo returned an empty list)rV   getaddrinfor   r   r   SOCK_STREAM
setsockoptIPPROTO_TCPTCP_NODELAYr   
SOL_SOCKETSO_KEEPALIVEr   itemsrj   r   r  rX   r  r   )r/   errresfamilysocktypeproto	canonnameZsocket_addressrs   r   r   r   r0   r0   r1   r    s6       


zConnection._connectc                 C   s4   z| j  d| j }W n tk
r.   d}Y nX |S )N:r   )r   r   AttributeError)r/   
host_errorr0   r0   r1   _host_error  s
    
zConnection._host_errorc                 C   s   |   }t|jdkrXzd| d|jd  dW S  tk
rT   d|jd   Y S X nLz&d|jd  d| d	|jd  dW S  tk
r   d|jd   Y S X d S )
NrH   zError connecting to z.                         r   .zConnection Error: Error z connecting to . )r"  rK   rr   r   )r/   	exceptionr!  r0   r0   r1   r    s    
"zConnection._error_messagec                 C   s   | j |  | js| js| jr| jp0t| j| j}| }| jd|ddi z|  }W n0 t	k
r   | jd|d dd |  }Y nX t
|dkrtd| jr| dd	| j t
|  dkrtd
| jr| d| j t
|  dkrtddS )z=Initialize the connection, authenticate and select a databaseAUTHcheck_healthFrh   r(  OKzInvalid Username or PasswordZCLIENTZSETNAMEzError setting client nameZSELECTzInvalid DatabaseN)r'  )r  r   r   r   r   r   Zget_credentialssend_commandr   r   r#   r   r   r   r   )r/   Zcred_providerZ	auth_argsZauth_responser0   r0   r1   r   -  s,    zConnection.on_connectc                 G   s|   | j   | jdkrdS t | jkrNz| jtj W n t	k
rL   Y nX z| j
  W n t	k
rp   Y nX d| _dS )z!Disconnects from the Redis serverN)r  r   rY   r   r   r   shutdownrV   	SHUT_RDWRr  r   r/   rr   r0   r0   r1   r   U  s    

zConnection.disconnectc                 C   s*   | j ddd t|  dkr&tddS )z Send PING, expect PONG in returnZPINGFr)  ZPONGz#Bad response from PING health checkN)r+  r#   r   r   r   r0   r0   r1   
_send_pingg  s    zConnection._send_pingc                 C   s   |    dS )z Function to call when PING failsNr
  )r/   r   r0   r0   r1   _ping_failedm  s    zConnection._ping_failedc                 C   s(   | j r$t | jkr$| j| j| j dS )z3Check the health of the connection with a PING/PONGN)r   r   r   r   r  r/  r0  r   r0   r0   r1   r(  q  s    zConnection.check_healthTc              
   C   s   | j s|   |r|   z*t|tr,|g}|D ]}| j | q0W n tjk
rj   |   t	dY n t
k
r } zV|   t|jdkrd|jd  }}n|jd }|jd }td| d| dW 5 d}~X Y n tk
r   |    Y nX dS )	z2Send an already packed command to the Redis serverzTimeout writing to socketrH   UNKNOWNr   r$  z while writing to socket. r#  N)rY   r  r(  r3   r;   sendallrV   re   r   r   r  rK   rr   r   r   )r/   commandr(  itemr  rq   errmsgr0   r0   r1   send_packed_commandv  s,    


&zConnection.send_packed_commandc                 O   s"   | j | jj| |ddd dS )z+Pack and send a command to the Redis serverr(  Tr)  N)r6  r   r   rM   )r/   rr   r   r0   r0   r1   r+    s    

zConnection.send_commandc              
   C   sn   | j }|s|   |  }z| j|W S  tk
rh } z"|   td| d|j W 5 d}~X Y nX dS )z8Poll the socket to see if there's data that can be read.Error while reading from z: N)	rY   r  r"  r  r}   r  r   r   rr   )r/   re   rs   r!  r  r0   r0   r1   r}     s    zConnection.can_readc              
   C   s   |   }z| jj|d}W n tjk
rF   |   td| Y n\ tk
r } z"|   td| d|j	 W 5 d}~X Y n t
k
r   |    Y nX | jrt | j | _t|tr||S )z0Read the response from a previously sent commandr   zTimeout reading from r7  z : N)r"  r  r   rV   re   r   r   r  r   rr   r   r   r   r   r3   r   )r/   r   r!  rN   r  r0   r0   r1   r     s&    
zConnection.read_responsec                 G   s   | j j| S )r   )r   r   r.  r0   r0   r1   r     s    zConnection.pack_commandc           	      C   s   g }g }d}| j }|D ]}| jj| D ]r}t|}||ksL||ksLt|trh|r`|t| d}g }||kszt|tr|| q&|| ||7 }q&q|r|t| |S )z.Pack multiple commands into the Redis protocolr   )	r   r   r   rK   r3   r5   r   r   r   )	r/   commandsr   r   Zbuffer_lengthr   cmdchunkZchunklenr0   r0   r1   pack_commands  s0    
zConnection.pack_commands)T)r   )F)r=   rC   rD   rE   ri   r   r   r   r2   r   r   r   r   r  r  r   r  r  r"  r  r   r   r/  r0  r(  r6  r+  r}   r   r   r;  r0   r0   r0   r1   r   L  sb   Q!)(


r   c                       s.   e Zd ZdZd	 fdd	Z fddZ  ZS )
SSLConnectionzManages SSL connections to and from the Redis server(s).
    This class extends the Connection class, adding SSL functionality, and making
    use of ssl.SSLContext (https://docs.python.org/3/library/ssl.html#ssl.SSLContext)
    NrequiredFc                    s   t stdt jf | || _|| _|dkr6tj}n:t|t	rptjtj
tjd}||krhtd| || }|| _|| _|| _|| _|| _|| _|	| _|
| _|| _|| _dS )ae  Constructor

        Args:
            ssl_keyfile: Path to an ssl private key. Defaults to None.
            ssl_certfile: Path to an ssl certificate. Defaults to None.
            ssl_cert_reqs: The string value for the SSLContext.verify_mode (none, optional, required). Defaults to "required".
            ssl_ca_certs: The path to a file of concatenated CA certificates in PEM format. Defaults to None.
            ssl_ca_data: Either an ASCII string of one or more PEM-encoded certificates or a bytes-like object of DER-encoded certificates.
            ssl_check_hostname: If set, match the hostname during the SSL handshake. Defaults to False.
            ssl_ca_path: The path to a directory containing several CA certificates in PEM format. Defaults to None.
            ssl_password: Password for unlocking an encrypted private key. Defaults to None.

            ssl_validate_ocsp: If set, perform a full ocsp validation (i.e not a stapled verification)
            ssl_validate_ocsp_stapled: If set, perform a validation on a stapled ocsp response
            ssl_ocsp_context: A fully initialized OpenSSL.SSL.Context object to be used in verifying the ssl_ocsp_expected_cert
            ssl_ocsp_expected_cert: A PEM armoured string containing the expected certificate to be returned from the ocsp verification service.

        Raises:
            RedisError
        z$Python wasn't built with SSL supportN)noneoptionalr=  z+Invalid SSL Certificate Requirements Flag: )ssl_availabler   superr2   keyfilecertfilessl	CERT_NONEr3   r;   CERT_OPTIONALCERT_REQUIRED	cert_reqsca_certsca_dataca_pathcheck_hostnamecertificate_passwordssl_validate_ocspssl_validate_ocsp_stapledssl_ocsp_contextssl_ocsp_expected_cert)r/   Zssl_keyfileZssl_certfileZssl_cert_reqsZssl_ca_certsZssl_ca_datassl_check_hostnameZssl_ca_pathZssl_passwordrN  rO  rP  rQ  r   Z	CERT_REQSrp   r0   r1   r2     s6    $
zSSLConnection.__init__c           
         s  t   }t }| j|_| j|_| js.| jrD|j	| j| j| j
d | jdk	sb| jdk	sb| jdk	rx|j| j| j| jd |j|| jd}| jdkrtdkrtd| jr| jrtd| jr\d	dl}d
dlm} | jdkr|j|jj}|| j || j n| j}||| j |j |t!! }|"  |#| j| j$f |%  |&  |S | jdkrtrd
dlm'} ||| j| j$| j}	|	( r|S t)d|S )z Wrap the socket with SSL support)rC  rB  r   N)cafilecapathcadata)server_hostnameTFzcryptography is not installed.zKEither an OCSP staple or pure OCSP connection must be validated - not both.r   rH   )ocsp_staple_verifier)OCSPVerifierzocsp validation error)*rA  r  rD  create_default_contextrL  rH  verify_moderC  rB  load_cert_chainrM  rI  rK  rJ  load_verify_locationswrap_socketr   rN  r    r   rO  OpenSSLZocsprX  rP  ZSSLContextZSSLv23_METHODZuse_certificate_fileZuse_privatekey_fileZset_ocsp_client_callbackrQ  r   rV   Zrequest_ocspr  r   do_handshaker,  rY  Zis_validr   )
r/   rs   contextsslsockr_  rX  Z
staple_ctxconrY  orS  r0   r1   r  ,  sj    
   
zSSLConnection._connect)NNr=  NNFNNFFNN)r=   rC   rD   rE   r2   r  __classcell__r0   r0   rS  r1   r<    s               Cr<  c                   @   s\   e Zd Zdddddddddeedddddddfee dd	d
Zdd Zdd Z	dd Z
dS )UnixDomainSocketConnection r   Nr   r   Fr   r   c                 C   s
  |s|r|dk	rt dt | _|| _|| _|| _|| _|| _|| _	|| _
|	| _|
tkr^g }
|	rl|
t |
| _| jr|dkrtt d| _nt|| _| j|
 ntt d| _|| _d| _|| _t|||| _d| _|| _| | g | _d| _|  || _!dS )aB  
        Initialize a new UnixDomainSocketConnection.
        To specify a retry policy for specific errors, first set
        `retry_on_error` to a list of the error/s to retry on, then set
        `retry` to a valid `Retry` object.
        To retry on TimeoutError, `retry_on_timeout` can also be set to `True`.
        Nr   rH   r   r   )"r   r   r   r   pathr   r   r   r   r   rX   r   ri   r   r   r   r   r   r   r   r   r   r   r   r   r)   r   rY   r   r   r   r   r   r   )r/   ri  r   r   r   rX   r,   r-   r.   r   r   r   rW   r   r   r   r   r   r   r0   r0   r1   r2   q  sD    


z#UnixDomainSocketConnection.__init__c                 C   s.   d| j fd| jfg}| jr*|d| jf |S )Nri  r   r   )ri  r   r   r   r   r0   r0   r1   r     s    z&UnixDomainSocketConnection.repr_piecesc                 C   s,   t  t jt j}|| j || j |S )z&Create a Unix domain socket connection)rV   AF_UNIXr  rj   rX   r  ri  )r/   rs   r0   r0   r1   r    s    z#UnixDomainSocketConnection._connectc                 C   sR   t |jdkr(d| j d|jd  dS d|jd  d| j d|jd  dS d S )NrH   z!Error connecting to unix socket: r%  r   r#  r$  z connecting to unix socket: )rK   rr   ri  )r/   r&  r0   r0   r1   r    s    $z)UnixDomainSocketConnection._error_message)r=   rC   rD   ri   r   r   r   r2   r   r  r  r0   r0   r0   r1   rg  p  s0   Grg  )0FFALSENNOc                 C   s6   | d ks| dkrd S t | tr.|  tkr.dS t| S )Nrh  F)r3   r;   upperFALSE_STRINGSr6   )r>   r0   r0   r1   to_bool  s
    rr  )	r   rX   r   r   r   r   max_connectionsr   rR  c              
   C   s  |  ds&|  ds&|  ds&tdt| } i }t| j D ]v\}}|r@t|dkr@t|d }t	|}|rz||||< W q t
tfk
r   td| dY qX q@|||< q@| jrt| j|d< | jrt| j|d	< | jd
kr| jrt| j|d< t|d< n| jr"t| j|d< | jr8t| j|d< | jrd|krztt| jdd|d< W n ttfk
r   Y nX | jdkrt|d< |S )Nzredis://z	rediss://zunix://zRRedis URL must specify one of the following schemes (redis://, rediss://, unix://)r   zInvalid value for `z` in connection URL.r   r   unixri  connection_classr   r   r   /rh  Zrediss)
startswith
ValueErrorr   r
   queryr  rK   r   URL_QUERY_ARGUMENT_PARSERSrM   r   r   r   schemeri  rg  hostnamer   r7   r   r   r<  )urlr   namer>   parserr0   r0   r1   	parse_url  sR    


r  c                   @   s   e Zd ZdZedd Ze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dZdddddZdS )ConnectionPoola  
    Create a connection pool. ``If max_connections`` is set, then this
    object raises :py:class:`~redis.exceptions.ConnectionError` when the pool's
    limit is reached.

    By default, TCP connections are created unless ``connection_class``
    is specified. Use class:`.UnixDomainSocketConnection` for
    unix sockets.

    Any additional keyword arguments are passed to the constructor of
    ``connection_class``.
    c                 K   s0   t |}d|kr|d |d< || | f |S )a  
        Return a connection pool configured from the given URL.

        For example::

            redis://[[username]:[password]]@localhost:6379/0
            rediss://[[username]:[password]]@localhost:6379/0
            unix://[username@]/path/to/socket.sock?db=0[&password=password]

        Three URL schemes are supported:

        - `redis://` creates a TCP socket connection. See more at:
          <https://www.iana.org/assignments/uri-schemes/prov/redis>
        - `rediss://` creates a SSL wrapped TCP socket connection. See more at:
          <https://www.iana.org/assignments/uri-schemes/prov/rediss>
        - ``unix://``: creates a Unix Domain Socket connection.

        The username, password, hostname, path and all querystring values
        are passed through urllib.parse.unquote in order to replace any
        percent-encoded values with their corresponding characters.

        There are several ways to specify a database number. The first value
        found will be used:

            1. A ``db`` querystring option, e.g. redis://localhost?db=0
            2. If using the redis:// or rediss:// schemes, the path argument
               of the url, e.g. redis://localhost/0
            3. A ``db`` keyword argument to this function.

        If none of these options are specified, the default db=0 is used.

        All querystring options are cast to their appropriate Python types.
        Boolean arguments can be specified with string values "True"/"False"
        or "Yes"/"No". Values that cannot be properly cast cause a
        ``ValueError`` to be raised. Once parsed, the querystring arguments
        and keyword arguments are passed to the ``ConnectionPool``'s
        class initializer. In the case of conflicting arguments, querystring
        arguments always win.
        ru  )r  update)clsr}  r   Zurl_optionsr0   r0   r1   from_url0  s
    )
zConnectionPool.from_urlNc                 K   sJ   |pd}t |tr|dk r"td|| _|| _|| _t | _| 	  d S )Nl        r   z,"max_connections" must be a positive integer)
r3   r7   rx  ru  connection_kwargsrs  	threadingLock
_fork_lockreset)r/   ru  rs  r  r0   r0   r1   r2   a  s    

zConnectionPool.__init__c                 C   s$   t | j dt| jf | j dS )Nr   r   )r<   r=   r9   ru  r  r   r0   r0   r1   r   w  s    "zConnectionPool.__repr__c                 C   s,   t  | _d| _g | _t | _t | _	d S )Nr   )
r  r  _lock_created_connections_available_connectionsset_in_use_connectionsr   r   r   r   r0   r0   r1   r  }  s
    
zConnectionPool.resetc                 C   sP   | j t krL| jjdd}|s$tz| j t kr<|   W 5 | j  X d S )N   )re   )r   r   r   r  acquirer   releaser  )r/   Zacquiredr0   r0   r1   	_checkpid  s    #zConnectionPool._checkpidc              	   O   s   |    | j> z| j }W n tk
r:   |  }Y nX | j| W 5 Q R X zZ|  z|	 rnt
dW n8 t
tfk
r   |  |  |	 rt
dY nX W n  tk
r   | |  Y nX |S )zGet a connection from the poolConnection has dataConnection not ready)r  r  r  pop
IndexErrormake_connectionr  addr  r}   r   r  r   r   r  r/   command_namekeysoptionsr   r0   r0   r1   get_connection  s*    
zConnectionPool.get_connectionc                 C   s,   | j }t|dd|dd|dddS )z,Return an encoder based on encoding settingsr,   r   r-   r   r.   Fr+   )r  r)   rM   )r/   r   r0   r0   r1   get_encoder  s    


zConnectionPool.get_encoderc                 C   s0   | j | jkrtd|  j d7  _ | jf | jS )zCreate a new connectionzToo many connectionsrH   )r  rs  r   ru  r  r   r0   r0   r1   r    s    zConnectionPool.make_connectionc              	   C   s   |    | jh z| j| W n tk
r4   Y nX | |rN| j| n$|  jd8  _|	  W 5 Q R  dS W 5 Q R X dS )z(Releases the connection back to the poolrH   N)
r  r  r  removeKeyErrorowns_connectionr  r   r  r   r   r0   r0   r1   r    s    
zConnectionPool.releasec                 C   s   |j | j kS r*   )r   r   r0   r0   r1   r    s    zConnectionPool.owns_connectionTc              	   C   sJ   |    | j2 |r$t| j| j}n| j}|D ]}|  q.W 5 Q R X dS )z
        Disconnects connections in the pool

        If ``inuse_connections`` is True, disconnect connections that are
        current in use, potentially by other threads. Otherwise only disconnect
        connections that are idle in the pool.
        N)r  r  r   r  r  r   )r/   Zinuse_connectionsconnectionsr   r0   r0   r1   r     s     zConnectionPool.disconnectr   )r   r^   c                 C   s8   | j d|i | jD ]
}||_q| jD ]
}||_q(d S )Nr   )r  r  r  r   r  )r/   r   connr0   r0   r1   	set_retry  s
    

zConnectionPool.set_retry)T)r=   rC   rD   rE   classmethodr  r   r2   r   r  r  r  r  r  r  r  r   r  r0   r0   r0   r1   r  "  s    
1 
/!	
r  c                       sR   e Zd ZdZddeef fdd	Zdd Zdd	 Zd
d Z	dd Z
dd Z  ZS )BlockingConnectionPoola  
    Thread-safe blocking connection pool::

        >>> from redis.client import Redis
        >>> client = Redis(connection_pool=BlockingConnectionPool())

    It performs the same function as the default
    :py:class:`~redis.ConnectionPool` implementation, in that,
    it maintains a pool of reusable connections that can be shared by
    multiple redis clients (safely across threads if required).

    The difference is that, in the event that a client tries to get a
    connection from the pool when all of connections are in use, rather than
    raising a :py:class:`~redis.ConnectionError` (as the default
    :py:class:`~redis.ConnectionPool` implementation does), it
    makes the client wait ("blocks") for a specified number of seconds until
    a connection becomes available.

    Use ``max_connections`` to increase / decrease the pool size::

        >>> pool = BlockingConnectionPool(max_connections=10)

    Use ``timeout`` to tell it either how many seconds to wait for a connection
    to become available, or to block forever:

        >>> # Block forever.
        >>> pool = BlockingConnectionPool(timeout=None)

        >>> # Raise a ``ConnectionError`` after five seconds if a connection is
        >>> # not available.
        >>> pool = BlockingConnectionPool(timeout=5)
    2      c                    s(   || _ || _t jf ||d| d S )N)ru  rs  )queue_classre   rA  r2   )r/   rs  re   ru  r  r  rS  r0   r1   r2   D  s    	zBlockingConnectionPool.__init__c                 C   sN   |  | j| _z| jd  W q tk
r6   Y q:Y qX qg | _t | _d S r*   )	r  rs  pool
put_nowaitr   _connectionsr   r   r   r   r0   r0   r1   r  U  s    zBlockingConnectionPool.resetc                 C   s   | j f | j}| j| |S )zMake a fresh connection.)ru  r  r  r   r   r0   r0   r1   r  m  s    z&BlockingConnectionPool.make_connectionc              	   O   s   |    d}z| jjd| jd}W n tk
r>   tdY nX |dkrP|  }zZ|  z| rltdW n8 tt	fk
r   |
  |  | rtdY nX W n  tk
r   | |  Y nX |S )a7  
        Get a connection, blocking for ``self.timeout`` until a connection
        is available from the pool.

        If the connection returned is ``None`` then creates a new connection.
        Because we use a last-in first-out queue, the existing connections
        (having been returned to the pool after the initial ``None`` values
        were added) will be returned before ``None`` values. This means we only
        create new connections when we need to, i.e.: the actual number of
        connections will only increase in response to demand.
        NT)blockre   zNo connection available.r  r  )r  r  rM   re   r   r   r  r  r}   r  r   r   r  r  r0   r0   r1   r  s  s,    
z%BlockingConnectionPool.get_connectionc                 C   sT   |    | |s*|  | jd dS z| j| W n tk
rN   Y nX dS )z)Releases the connection back to the pool.N)r  r  r   r  r  r   r   r0   r0   r1   r    s    
zBlockingConnectionPool.releasec                 C   s    |    | jD ]}|  qdS )z(Disconnects all connections in the pool.N)r  r  r   r   r0   r0   r1   r     s    
z!BlockingConnectionPool.disconnect)r=   rC   rD   rE   r   r   r2   r  r  r  r  r   rf  r0   r0   rS  r1   r  "  s   #4r  )br   rq   rZ   r   rV   r   r  r  r   	itertoolsr   queuer   r   r   r   typingr   r	   urllib.parser
   r   r   Zredis.backoffr   Zredis.credentialsr   r   Zredis.exceptionsr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   Zredis.retryr   Zredis.utilsr    r!   r"   r#   rD  r@  ImportErrorBlockingIOErrorEWOULDBLOCKro   hasattrr$   SSLWantWriteErrorSSLErrorr   r  rn   r   r   r   r   r   rl   r   ri   rP   rR   rS   rQ   rT   r)   rF   rU   r   r   __annotations__r   r   r   r   r<  rg  rq  rr  r7   r8   r   rz  r  r  r  r0   r0   r0   r1   <module>   s    D





)( 	_m3    a	9  