U
    dg                     @   s  U d 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 ddlmZ ddlm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 ddlmZ ddl m!Z! G dd de
Z"e"e#d Z$e$j%e$j&fZ'ee(e(f e)d< eee gee f Z*G dd dZ+ej,e$j&ej-e$j.ej/e$j0ej1e$j0ej2e$j0iZ3e+eddddZ4e+eee5ef  e+dddZ6e	e!ef ee5 eee( ee ee(ee5 ee( ee f dddZ7e	e!ef ee5 ee(dddZ8e	e!ef ee5 eee(ee5 f ddd Z9ee!ef e(d!d"d#Z:d$S )%z*Represent a deployment of MongoDB servers.    )sample)
AnyCallableDictListMappingMutableMapping
NamedTupleOptionalTuplecastMinKey)ObjectId)common)ConfigurationError)ReadPreference_AggWritePref_ServerModeServerDescription)	Selection)SERVER_TYPE)_Addressc                   @   s>   e Zd ZU eed< eed< eed< eed< eed< eed< dS )_TopologyTypeSingleReplicaSetNoPrimaryReplicaSetWithPrimaryShardedUnknownLoadBalancedN)__name__
__module____qualname__int__annotations__ r&   r&   @/tmp/pip-unpacked-wheel-oblwsawz/pymongo/topology_description.pyr   +   s   
r      SRV_POLLING_TOPOLOGIESc                   @   s  e Zd Zeeeef ee ee ee	 e
ddddZddddZdddd	Zeed
ddZed d
ddZd dddZeeef dddZeedddZeedddZeee dddZeee dddZeee	 dddZeee dddZeee ddd Zeedd!d"Zeee dd#d$Zeee dd%d&Zeedd'd(Zeedd)d*Z ee! ee d+d,d-Z"d8e
ee ee# ee d.d/d0Z$e%j&fe'ed1d2d3Z(edd4d5Z)edd6d7Z*dS )9TopologyDescriptionN)topology_typeserver_descriptionsreplica_set_namemax_set_versionmax_election_idtopology_settingsreturnc                 C   s   || _ || _|| _|| _|| _|| _d| _| j tjkr>| 	  | j
}|sPd| _n.tdd |D rjd| _ntdd |D | _dS )a  Representation of a deployment of MongoDB servers.

        :Parameters:
          - `topology_type`: initial type
          - `server_descriptions`: dict of (address, ServerDescription) for
            all seeds
          - `replica_set_name`: replica set name or None
          - `max_set_version`: greatest setVersion seen from a primary, or None
          - `max_election_id`: greatest electionId seen from a primary, or None
          - `topology_settings`: a TopologySettings
        Nc                 s   s   | ]}|j d kV  qd S Nlogical_session_timeout_minutes.0sr&   r&   r'   	<genexpr>j   s     z/TopologyDescription.__init__.<locals>.<genexpr>c                 s   s   | ]}|j V  qd S r2   r3   r5   r&   r&   r'   r8   m   s    )_topology_type_replica_set_name_server_descriptions_max_set_version_max_election_id_topology_settings_incompatible_errTOPOLOGY_TYPEr    _init_incompatible_errreadable_servers_ls_timeout_minutesanymin)selfr+   r,   r-   r.   r/   r0   rB   r&   r&   r'   __init__>   s"    	zTopologyDescription.__init__)r1   c                 C   s   | j  D ]}|jsq
|jdk	o*|jtjk}|jdk	o@|jtjk }|rnd|jd |jd p\d|jtjf | _	q
|r
d|jd |jd pd|jtjtj
f | _	 qq
dS )z>Internal compatibility check for non-load balanced topologies.Nz]Server at %s:%d requires wire version %d, but this version of PyMongo only supports up to %d.r      zgServer at %s:%d reports wire version %d, but this version of PyMongo requires at least %d (MongoDB %s).)r;   valuesis_server_type_knownmin_wire_versionr   ZMAX_SUPPORTED_WIRE_VERSIONmax_wire_versionZMIN_SUPPORTED_WIRE_VERSIONaddressr?   ZMIN_SUPPORTED_SERVER_VERSION)rF   r7   Zserver_too_newZserver_too_oldr&   r&   r'   rA   q   s:    



z*TopologyDescription._init_incompatible_errc                 C   s   | j rt| j dS )zRaise ConfigurationError if any server is incompatible.

        A server is incompatible if its wire protocol version range does not
        overlap with PyMongo's.
        N)r?   r   rF   r&   r&   r'   check_compatible   s    z$TopologyDescription.check_compatible)rM   r1   c                 C   s
   || j kS r2   )r;   )rF   rM   r&   r&   r'   
has_server   s    zTopologyDescription.has_serverc                 C   s   | j |  }t| |S )z;A copy of this description, with one server marked Unknown.)r;   
to_unknownupdated_topology_description)rF   rM   Z
unknown_sdr&   r&   r'   reset_server   s    z TopologyDescription.reset_serverc                 C   sD   | j tjkrtj}n| j }dd | jD }t||| j| j| j| j	S )z<A copy of this description, with all servers marked Unknown.c                 S   s   i | ]}|t |qS r&   r   )r6   rM   r&   r&   r'   
<dictcomp>   s      z-TopologyDescription.reset.<locals>.<dictcomp>)
r9   r@   r   r   r;   r*   r:   r<   r=   r>   )rF   r+   sdsr&   r&   r'   reset   s    zTopologyDescription.resetc                 C   s
   | j  S )z[Dict of (address,
        :class:`~pymongo.server_description.ServerDescription`).
        )r;   copyrN   r&   r&   r'   r,      s    z'TopologyDescription.server_descriptionsc                 C   s   | j S )zThe type of this topology.)r9   rN   r&   r&   r'   r+      s    z!TopologyDescription.topology_typec                 C   s   t j| j S )zUThe topology type as a human readable string.

        .. versionadded:: 3.4
        )r@   _fieldsr9   rN   r&   r&   r'   topology_type_name   s    z&TopologyDescription.topology_type_namec                 C   s   | j S )zThe replica set name.)r:   rN   r&   r&   r'   r-      s    z$TopologyDescription.replica_set_namec                 C   s   | j S )z1Greatest setVersion seen from a primary, or None.)r<   rN   r&   r&   r'   r.      s    z#TopologyDescription.max_set_versionc                 C   s   | j S )z1Greatest electionId seen from a primary, or None.)r=   rN   r&   r&   r'   r/      s    z#TopologyDescription.max_election_idc                 C   s   | j S )z)Minimum logical session timeout, or None.)rC   rN   r&   r&   r'   r4      s    z3TopologyDescription.logical_session_timeout_minutesc                 C   s   dd | j  D S )z)List of Servers of types besides Unknown.c                 S   s   g | ]}|j r|qS r&   rJ   r5   r&   r&   r'   
<listcomp>   s      z5TopologyDescription.known_servers.<locals>.<listcomp>r;   rI   rN   r&   r&   r'   known_servers   s    z!TopologyDescription.known_serversc                 C   s   t dd | j D S )z7Whether there are any Servers of types besides Unknown.c                 s   s   | ]}|j r|V  qd S r2   rZ   r5   r&   r&   r'   r8      s      z8TopologyDescription.has_known_servers.<locals>.<genexpr>)rD   r;   rI   rN   r&   r&   r'   has_known_servers   s    z%TopologyDescription.has_known_serversc                 C   s   dd | j  D S )zList of readable Servers.c                 S   s   g | ]}|j r|qS r&   )Zis_readabler5   r&   r&   r'   r[      s      z8TopologyDescription.readable_servers.<locals>.<listcomp>r\   rN   r&   r&   r'   rB      s    z$TopologyDescription.readable_serversc                 C   s"   | j }|rtdd | j D S dS )z3Minimum of all servers' max wire versions, or None.c                 s   s   | ]}|j V  qd S r2   )rL   r5   r&   r&   r'   r8      s     z:TopologyDescription.common_wire_version.<locals>.<genexpr>N)r]   rE   rF   Zserversr&   r&   r'   common_wire_version   s    z'TopologyDescription.common_wire_versionc                 C   s   | j jS r2   )r>   heartbeat_frequencyrN   r&   r&   r'   ra     s    z'TopologyDescription.heartbeat_frequencyc                 C   s   | j jS r2   )r>   Z_srv_max_hostsrN   r&   r&   r'   srv_max_hosts  s    z!TopologyDescription.srv_max_hosts)	selectionr1   c                    s>   |sg S t dd |jD  | jjd  fdd|jD S )Nc                 s   s   | ]}t t|jV  qd S r2   r   floatZround_trip_timer5   r&   r&   r'   r8     s     z=TopologyDescription._apply_local_threshold.<locals>.<genexpr>g     @@c                    s$   g | ]}t t|j  kr|qS r&   rd   r5   Zfastest	thresholdr&   r'   r[     s   z>TopologyDescription._apply_local_threshold.<locals>.<listcomp>)rE   r,   r>   Zlocal_threshold_ms)rF   rc   r&   rf   r'   _apply_local_threshold  s    z*TopologyDescription._apply_local_threshold)selectorrM   custom_selectorr1   c                 C   s   t |ddr4| j}|r4||jk r4td||j|f t|trH||  | jtj	krXg S | jtj
tjfkrp| jS |r|  |}|r|gS g S t| }| jtjkr||}|dk	r|r|||j}| |S )a  List of servers matching the provided selector(s).

        :Parameters:
          - `selector`: a callable that takes a Selection as input and returns
            a Selection as output. For example, an instance of a read
            preference from :mod:`~pymongo.read_preferences`.
          - `address` (optional): A server address to select.
          - `custom_selector` (optional): A callable that augments server
            selection rules. Accepts a list of
            :class:`~pymongo.server_description.ServerDescription` objects and
            return a list of server descriptions that should be considered
            suitable for the desired operation.

        .. versionadded:: 3.4
        rK   r   zF%s requires min wire version %d, but topology's min wire version is %dN)getattrr`   rK   r   
isinstancer   Zselection_hookr+   r@   r   r   r    r]   r,   getr   Zfrom_topology_descriptionr   Zwith_server_descriptionsrh   )rF   ri   rM   rj   Z	common_wvdescriptionrc   r&   r&   r'   apply_selector  s2    



z"TopologyDescription.apply_selector)read_preferencer1   c                 C   s   t d| t| |S )a  Does this topology have any readable servers available matching the
        given read preference?

        :Parameters:
          - `read_preference`: an instance of a read preference from
            :mod:`~pymongo.read_preferences`. Defaults to
            :attr:`~pymongo.read_preferences.ReadPreference.PRIMARY`.

        .. note:: When connected directly to a single server this method
          always returns ``True``.

        .. versionadded:: 3.4
        rp   )r   Zvalidate_read_preferencerD   ro   )rF   rp   r&   r&   r'   has_readable_serverM  s    z'TopologyDescription.has_readable_serverc                 C   s   |  tjS )zDoes this topology have a writable server available?

        .. note:: When connected directly to a single server this method
          always returns ``True``.

        .. versionadded:: 3.4
        )rq   r   PRIMARYrN   r&   r&   r'   has_writable_server^  s    z'TopologyDescription.has_writable_serverc                 C   s0   t | j dd d}d| jj| jj| j|S )Nc                 S   s   | j S r2   )rM   )sdr&   r&   r'   <lambda>j      z.TopologyDescription.__repr__.<locals>.<lambda>)keyz-<{} id: {}, topology_type: {}, servers: {!r}>)	sortedr;   rI   format	__class__r!   r>   Z_topology_idrY   r_   r&   r&   r'   __repr__h  s    zTopologyDescription.__repr__)NN)+r!   r"   r#   r$   r   r   r   r
   strr   r   rG   rA   rO   boolrP   rS   rV   r,   propertyr+   rY   r-   r.   r/   r4   r   r]   r^   rB   r`   ra   rb   r   rh   _ServerSelectorro   r   rr   r   rq   rs   r{   r&   r&   r&   r'   r*   =   sd   
3/	  6
r*   )topology_descriptionserver_descriptionr1   c           
      C   s  |j }| j}| j}| j}| j}|j}|  }|||< |tjkr|dk	rr||jkrrt	d
||j}	|j|	d||< ttj||||| jS |tjkr|tjtjfkrt| jjdkrtj}q|| n|tjtjfkrt| }|tjkr|tjtjfkr|| n|tjkr|tjtjfkr2|| nL|tjkrXt|||||\}}}}n&|tjtjtjfkrt|||\}}n|tj kr|tjtjfkr|| t!|}nR|tjkrt|||||\}}}}n,|tjtjtjfkrt"|||}nt!|}t|||||| jS )a}  Return an updated copy of a TopologyDescription.

    :Parameters:
      - `topology_description`: the current TopologyDescription
      - `server_description`: a new ServerDescription that resulted from
        a hello call

    Called after attempting (successfully or not) to call hello on the
    server at server_description.address. Does not modify topology_description.
    Nzeclient is configured to connect to a replica set named '{}' but this node belongs to a set named '{}')errorrH   )#rM   r+   r-   r.   r/   server_typer,   r@   r   r   ry   rQ   r*   r>   r   r   Z
StandaloneZLoadBalancerlenZseedspopZRSGhost_SERVER_TYPE_TO_TOPOLOGY_TYPEr   Mongosr   	RSPrimary_update_rs_from_primaryRSSecondary	RSArbiterRSOther!_update_rs_no_primary_from_memberr   _check_has_primary#_update_rs_with_primary_from_member)
r   r   rM   r+   set_namer.   r/   r   rU   r   r&   r&   r'   rR     s    
 	
      


    rR   )r   seedlistr1   c                 C   s   | j tkst|  }t| t|kr.| S t| D ]}||kr:|| q:| jdkrt|t|  }| jt	| }|dkrt
t|t|t	|}ng }|D ]}||krt|||< qt| j || j| j| j| jS )zReturn an updated copy of a TopologyDescription.

    :Parameters:
      - `topology_description`: the current TopologyDescription
      - `seedlist`: a list of new seeds new ServerDescription that resulted from
        a hello call
    r   )r+   r)   AssertionErrorr,   setkeyslistr   rb   r   r   rx   rE   r   r*   r-   r.   r/   r>   )r   r   rU   rM   Z	new_hostsZn_to_addr&   r&   r'   )_updated_topology_description_srv_polling  s0    

r   )rU   r-   r   r.   r/   r1   c                 C   s  |dkr|j }n&||j kr6| |j t| |||fS |jdksJ|jdk r|j|jf}||f}d|krd|kr||k r| | |j< t| |||fS |j}|jdk	r|dks|j|kr|j}nl|j|jf}||f}tdd |D }tdd |D }||k r| | |j< t| |||fS |j}|j}| 	 D ]6}	|	j
tjkr2|	j|jkr2|	 | |	j<  qjq2|jD ]}
|
| krpt|
| |
< qpt| |j D ]}| | qt| |||fS )ag  Update topology description from a primary's hello response.

    Pass in a dict of ServerDescriptions, current replica set name, the
    ServerDescription we are processing, and the TopologyDescription's
    max_set_version and max_election_id if any.

    Returns (new topology type, new replica_set_name, new max_set_version,
    new max_election_id).
    N   c                 s   s    | ]}|d krt  n|V  qd S r2   r   r6   ir&   r&   r'   r8   >  s     z*_update_rs_from_primary.<locals>.<genexpr>c                 s   s    | ]}|d krt  n|V  qd S r2   r   r   r&   r&   r'   r8   ?  s     )r-   r   rM   r   rL   Zset_versionZelection_idrQ   tuplerI   r   r   r   	all_hostsr   r   )rU   r-   r   r.   r/   Znew_election_tupleZmax_election_tupleZnew_election_safeZmax_election_safeserverZnew_addressaddrr&   r&   r'   r     sR    







r   )rU   r-   r   r1   c                 C   sJ   |dk	st ||jkr$| |j n|jrB|j|jkrB| |j t| S )zRS with known primary. Process a response from a non-primary.

    Pass in a dict of ServerDescriptions, current replica set name, and the
    ServerDescription we are processing.

    Returns new topology type.
    N)r   r-   r   rM   mer   )rU   r-   r   r&   r&   r'   r   c  s    
r   c                 C   sz   t j}|dkr|j}n||jkr4| |j ||fS |jD ]}|| kr:t|| |< q:|jrr|j|jkrr| |j ||fS )zRS without known primary. Update from a non-primary's response.

    Pass in a dict of ServerDescriptions, current replica set name, and the
    ServerDescription we are processing.

    Returns (new topology type, new replica_set_name).
    N)r@   r   r-   r   rM   r   r   r   )rU   r-   r   r+   rM   r&   r&   r'   r   z  s    

r   )rU   r1   c                 C   s*   |   D ]}|jtjkrtj  S qtjS )zCurrent topology type is ReplicaSetWithPrimary. Is primary still known?

    Pass in a dict of ServerDescriptions.

    Returns new topology type.
    N)rI   r   r   r   r@   r   r   )rU   r7   r&   r&   r'   r     s    r   N);__doc__randomr   typingr   r   r   r   r   r   r	   r
   r   r   Zbson.min_keyr   Zbson.objectidr   Zpymongor   Zpymongo.errorsr   Zpymongo.read_preferencesr   r   r   Zpymongo.server_descriptionr   Zpymongo.server_selectorsr   Zpymongo.server_typer   Zpymongo.typingsr   r   ranger@   r   r   r)   r$   r%   r   r*   r   r   r   r   r   r   r   r   rR   r|   r   r   r   r   r   r&   r&   r&   r'   <module>   sn   0	  ;      j -
P

 