U
    ~fh`S                     @  s  d Z ddlmZ ddlmZ ddl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mZ er|ddlmZ dd	lmZ dZd
ZdZdZdZdZeeef Ze
eeef  ZdddddZdddddZdddddZ dddddZ!G dd  d Z"G d!d" d"e"Z#G d#d$ d$e"Z$G d%d& d&e"Z%G d'd( d(e"Z&G d)d* d*e"Z'G d+d, d,Z(e#e$e%e&e'fZ)d:dddd d.d/d0Z*d1Z+G d2d3 d3Z,ddd4d5d6Z-G d7d8 d8Z.d9S );zUtilities for choosing which member of a replica set to read from.

.. seealso:: This module is compatible with both the synchronous and asynchronous PyMongo APIs.
    )annotations)abc)TYPE_CHECKINGAnyMappingOptionalSequence)max_staleness_selectors)ConfigurationError) member_with_tags_server_selector#secondary_with_tags_server_selector)	Selection)TopologyDescription            )primaryZprimaryPreferredZ	secondaryZsecondaryPreferredZnearestOptional[_TagSets])tag_setsreturnc                 C  st   | dkr| S t | ttfs*td| dt| dkrFtd| d| D ] }t |tjsJtd|dqJt| S )z$Validate tag sets for a MongoClient.Nz	Tag sets z invalid, must be a sequencer   z: invalid, must be None or contain at least one set of tagszTag set zg invalid, must be an instance of dict, bson.son.SON or other type that inherits from collection.Mapping)
isinstancelisttuple	TypeErrorlen
ValueErrorr   r   )r   tags r   </tmp/pip-unpacked-wheel-36gvocj8/pymongo/read_preferences.py_validate_tag_sets8   s    

r    r   str)max_stalenessr   c                 C  s   d|  S )Nz6maxStalenessSeconds must be a positive integer, not %sr   r"   r   r   r   _invalid_max_staleness_msgO   s    r$   intc                 C  s:   | dkrdS t | ts"tt| | dkr6tt| | S )zValidate max_staleness.r   )r   r%   r   r$   r   r#   r   r   r   _validate_max_stalenessT   s    
r'   Optional[_Hedge])hedger   c                 C  s(   | dkrdS t | ts$td| | S )zValidate hedge.Nz hedge must be a dictionary, not )r   dictr   )r)   r   r   r   _validate_hedgeb   s
    
r+   c                   @  s  e Zd ZdZdZd3dddddd	d
dZeddddZeddddZeddddZ	eddddZ
eddddZeddddZeddddZeddddZddd d!Zd"d#d$d%d&Zd"d#d$d'd(Zddd)d*Zd+dd,d-d.Zd/d/d0d1d2ZdS )4_ServerModez$Base class for all read preferences.)Z__mongos_modeZ__modeZ
__tag_setsZ__max_stalenessZ__hedgeNr&   r%   r   r(   None)moder   r"   r)   r   c                 C  s2   t | | _|| _t|| _t|| _t|| _d S N)	_MONGOS_MODES_ServerMode__mongos_mode_ServerMode__moder    _ServerMode__tag_setsr'   _ServerMode__max_stalenessr+   _ServerMode__hedge)selfr.   r   r"   r)   r   r   r   __init__r   s
    


z_ServerMode.__init__r!   r   c                 C  s   | j jS )z!The name of this read preference.)	__class____name__r6   r   r   r   name   s    z_ServerMode.namec                 C  s   | j S )z(The mongos mode of this read preference.)r1   r;   r   r   r   mongos_mode   s    z_ServerMode.mongos_modezdict[str, Any]c                 C  sT   d| j i}| jdi gfkr$| j|d< | jdkr8| j|d< | jdi fkrP| j|d< |S )zRead preference as a document.r.   Nr   r&   ZmaxStalenessSecondsr)   )r1   r3   r4   r5   )r6   docr   r   r   document   s    




z_ServerMode.documentc                 C  s   | j S )z*The mode of this read preference instance.)r2   r;   r   r   r   r.      s    z_ServerMode.mode_TagSetsc                 C  s   | j rt| j S i gS )aW  Set ``tag_sets`` to a list of dictionaries like [{'dc': 'ny'}] to
        read only from members whose ``dc`` tag has the value ``"ny"``.
        To specify a priority-order for tag sets, provide a list of
        tag sets: ``[{'dc': 'ny'}, {'dc': 'la'}, {}]``. A final, empty tag
        set, ``{}``, means "read from any member that matches the mode,
        ignoring tags." MongoClient tries each set of tags in turn
        until it finds a set of tags with at least one matching member.
        For example, to only send a query to an analytic node::

           Nearest(tag_sets=[{"node":"analytics"}])

        Or using :class:`SecondaryPreferred`::

           SecondaryPreferred(tag_sets=[{"node":"analytics"}])

           .. seealso:: `Data-Center Awareness
               <https://www.mongodb.com/docs/manual/data-center-awareness/>`_
        )r3   r   r;   r   r   r   r      s    z_ServerMode.tag_setsc                 C  s   | j S )zThe maximum estimated length of time (in seconds) a replica set
        secondary can fall behind the primary in replication before it will
        no longer be selected for operations, or -1 for no maximum.
        r4   r;   r   r   r   r"      s    z_ServerMode.max_stalenessc                 C  s   | j S )a  The read preference ``hedge`` parameter.

        A dictionary that configures how the server will perform hedged reads.
        It consists of the following keys:

        - ``enabled``: Enables or disables hedged reads in sharded clusters.

        Hedged reads are automatically enabled in MongoDB 4.4+ when using a
        ``nearest`` read preference. To explicitly enable hedged reads, set
        the ``enabled`` key  to ``true``::

            >>> Nearest(hedge={'enabled': True})

        To explicitly disable hedged reads, set the ``enabled`` key  to
        ``False``::

            >>> Nearest(hedge={'enabled': False})

        .. versionadded:: 3.11
        )r5   r;   r   r   r   r)      s    z_ServerMode.hedgec                 C  s   | j dkrdS dS )a  The wire protocol version the server must support.

        Some read preferences impose version requirements on all servers (e.g.
        maxStalenessSeconds requires MongoDB 3.4 / maxWireVersion 5).

        All servers' maxWireVersion must be at least this read preference's
        `min_wire_version`, or the driver raises
        :exc:`~pymongo.errors.ConfigurationError`.
        r&   r      rA   r;   r   r   r   min_wire_version   s    z_ServerMode.min_wire_versionc                 C  s   d | j| j| j| jS )Nz1{}(tag_sets={!r}, max_staleness={!r}, hedge={!r}))formatr<   r3   r4   r5   r;   r   r   r   __repr__   s    z_ServerMode.__repr__r   boolotherr   c                 C  s>   t |tr:| j|jko8| j|jko8| j|jko8| j|jkS tS r/   )r   r,   r.   r   r"   r)   NotImplementedr6   rH   r   r   r   __eq__   s    



z_ServerMode.__eq__c                 C  s
   | |k S r/   r   rJ   r   r   r   __ne__   s    z_ServerMode.__ne__c                 C  s   | j | j| j| jdS )zeReturn value of object for pickling.

        Needed explicitly because __slots__() defined.
        )r.   r   r"   r)   )r2   r3   r4   r5   r;   r   r   r   __getstate__   s
    z_ServerMode.__getstate__zMapping[str, Any])valuer   c                 C  sD   |d | _ t| j  | _t|d | _t|d | _t|d | _dS )zRestore from pickling.r.   r   r"   r)   N)	r2   r0   r1   r    r3   r'   r4   r+   r5   )r6   rN   r   r   r   __setstate__   s
    
z_ServerMode.__setstate__r   	selectionr   c                 C  s   |S r/   r   r6   rQ   r   r   r   __call__  s    z_ServerMode.__call__)Nr&   N)r:   
__module____qualname____doc__	__slots__r7   propertyr<   r=   r?   r.   r   r"   r)   rC   rE   rK   rL   rM   rO   rS   r   r   r   r   r,   m   s8      
r,   c                      sX   e Zd ZdZdZdd fddZdddd	d
ZddddZdddddZ  Z	S )PrimaryaG  Primary read preference.

    * When directly connected to one mongod queries are allowed if the server
      is standalone or a replica set primary.
    * When connected to a mongos queries are sent to the primary of a shard.
    * When connected to a replica set queries are sent to the primary of
      the replica set.
    r   r-   r8   c                   s   t  t d S r/   )superr7   _PRIMARYr;   r9   r   r   r7     s    zPrimary.__init__r   rP   c                 C  s   |j S z*Apply this read preference to a Selection.)primary_selectionrR   r   r   r   rS     s    zPrimary.__call__r!   c                 C  s   dS )Nz	Primary()r   r;   r   r   r   rE     s    zPrimary.__repr__r   rF   rG   c                 C  s   t |tr|jtkS tS r/   )r   r,   r.   r[   rI   rJ   r   r   r   rK      s    

zPrimary.__eq__)
r:   rT   rU   rV   rW   r7   rS   rE   rK   __classcell__r   r   r\   r   rY   
  s   	rY   c                      sB   e Zd ZdZdZdddddd	 fd
dZdddddZ  ZS )PrimaryPreferreda  PrimaryPreferred read preference.

    * When directly connected to one mongod queries are allowed to standalone
      servers, to a replica set primary, or to replica set secondaries.
    * When connected to a mongos queries are sent to the primary of a shard if
      available, otherwise a shard secondary.
    * When connected to a replica set queries are sent to the primary if
      available, otherwise a secondary.

    .. note:: When a :class:`~pymongo.mongo_client.MongoClient` is first
      created reads will be routed to an available secondary until the
      primary of the replica set is discovered.

    :param tag_sets: The :attr:`~tag_sets` to use if the primary is not
        available.
    :param max_staleness: (integer, in seconds) The maximum estimated
        length of time a replica set secondary can fall behind the primary in
        replication before it will no longer be selected for operations.
        Default -1, meaning no maximum. If it is set, it must be at least
        90 seconds.
    :param hedge: The :attr:`~hedge` to use if the primary is not available.

    .. versionchanged:: 3.11
       Added ``hedge`` parameter.
    r   Nr&   r   r%   r(   r-   r   r"   r)   r   c                   s   t  t||| d S r/   )rZ   r7   _PRIMARY_PREFERREDr6   r   r"   r)   r\   r   r   r7   C  s    zPrimaryPreferred.__init__r   rP   c                 C  s&   |j r|jS t| jt| j|S dS (Apply this read preference to Selection.N)r   r^   r   r   r	   selectr"   rR   r   r   r   rS   K  s     zPrimaryPreferred.__call__)Nr&   Nr:   rT   rU   rV   rW   r7   rS   r_   r   r   r\   r   r`   &  s      r`   c                      sB   e Zd ZdZdZdddddd	 fd
dZdddddZ  ZS )	Secondarya  Secondary read preference.

    * When directly connected to one mongod queries are allowed to standalone
      servers, to a replica set primary, or to replica set secondaries.
    * When connected to a mongos queries are distributed among shard
      secondaries. An error is raised if no secondaries are available.
    * When connected to a replica set queries are distributed among
      secondaries. An error is raised if no secondaries are available.

    :param tag_sets: The :attr:`~tag_sets` for this read preference.
    :param max_staleness: (integer, in seconds) The maximum estimated
        length of time a replica set secondary can fall behind the primary in
        replication before it will no longer be selected for operations.
        Default -1, meaning no maximum. If it is set, it must be at least
        90 seconds.
    :param hedge: The :attr:`~hedge` for this read preference.

    .. versionchanged:: 3.11
       Added ``hedge`` parameter.
    r   Nr&   r   r%   r(   r-   ra   c                   s   t  t||| d S r/   )rZ   r7   
_SECONDARYrc   r\   r   r   r7   m  s    zSecondary.__init__r   rP   c                 C  s   t | jt| j|S re   )r   r   r	   rf   r"   rR   r   r   r   rS   u  s     zSecondary.__call__)Nr&   Nrg   r   r   r\   r   rh   U  s      rh   c                      sB   e Zd ZdZdZdddddd	 fd
dZdddddZ  ZS )SecondaryPreferreda  SecondaryPreferred read preference.

    * When directly connected to one mongod queries are allowed to standalone
      servers, to a replica set primary, or to replica set secondaries.
    * When connected to a mongos queries are distributed among shard
      secondaries, or the shard primary if no secondary is available.
    * When connected to a replica set queries are distributed among
      secondaries, or the primary if no secondary is available.

    .. note:: When a :class:`~pymongo.mongo_client.MongoClient` is first
      created reads will be routed to the primary of the replica set until
      an available secondary is discovered.

    :param tag_sets: The :attr:`~tag_sets` for this read preference.
    :param max_staleness: (integer, in seconds) The maximum estimated
        length of time a replica set secondary can fall behind the primary in
        replication before it will no longer be selected for operations.
        Default -1, meaning no maximum. If it is set, it must be at least
        90 seconds.
    :param hedge: The :attr:`~hedge` for this read preference.

    .. versionchanged:: 3.11
       Added ``hedge`` parameter.
    r   Nr&   r   r%   r(   r-   ra   c                   s   t  t||| d S r/   )rZ   r7   _SECONDARY_PREFERREDrc   r\   r   r   r7     s    zSecondaryPreferred.__init__r   rP   c                 C  s(   t | jt| j|}|r|S |jS dS rd   )r   r   r	   rf   r"   r^   )r6   rQ   Zsecondariesr   r   r   rS     s     zSecondaryPreferred.__call__)Nr&   Nrg   r   r   r\   r   rk   |  s      rk   c                      sB   e Zd ZdZdZdddddd	 fd
dZdddddZ  ZS )Nearestah  Nearest read preference.

    * When directly connected to one mongod queries are allowed to standalone
      servers, to a replica set primary, or to replica set secondaries.
    * When connected to a mongos queries are distributed among all members of
      a shard.
    * When connected to a replica set queries are distributed among all
      members.

    :param tag_sets: The :attr:`~tag_sets` for this read preference.
    :param max_staleness: (integer, in seconds) The maximum estimated
        length of time a replica set secondary can fall behind the primary in
        replication before it will no longer be selected for operations.
        Default -1, meaning no maximum. If it is set, it must be at least
        90 seconds.
    :param hedge: The :attr:`~hedge` for this read preference.

    .. versionchanged:: 3.11
       Added ``hedge`` parameter.
    r   Nr&   r   r%   r(   r-   ra   c                   s   t  t||| d S r/   )rZ   r7   _NEARESTrc   r\   r   r   r7     s    zNearest.__init__r   rP   c                 C  s   t | jt| j|S rj   )r   r   r	   rf   r"   rR   r   r   r   rS     s     zNearest.__call__)Nr&   Nrg   r   r   r\   r   rm     s      rm   c                   @  s`   e Zd ZdZdZddddZddd	d
dZdddddZddddZdddddZ	dS )_AggWritePrefzAgg $out/$merge write preference.

    * If there are readable servers and there is any pre-5.0 server, use
      primary read preference.
    * Otherwise use `pref` read preference.

    :param pref: The read preference to use on MongoDB 5.0+.
    )prefeffective_prefr,   rp   c                 C  s   || _ tj| _d S r/   )rp   ReadPreferencePRIMARYrq   )r6   rp   r   r   r   r7     s    z_AggWritePref.__init__r   r-   )topology_descriptionr   c                 C  s4   |j }|tjr(|r(|dk r(tj| _n| j| _d S )N   )Zcommon_wire_versionZhas_readable_serverrs   PRIMARY_PREFERREDrt   rq   rp   )r6   ru   Z	common_wvr   r   r   selection_hook  s    

z_AggWritePref.selection_hookr   rP   c                 C  s
   |  |S r]   )rq   rR   r   r   r   rS     s    z_AggWritePref.__call__r!   r8   c                 C  s   d| j dS )Nz_AggWritePref(pref=)rr   r;   r   r   r   rE     s    z_AggWritePref.__repr__r   r<   r   c                 C  s   t | j|S r/   )getattrrq   )r6   r<   r   r   r   __getattr__  s    z_AggWritePref.__getattr__N)
r:   rT   rU   rV   rW   r7   rx   rS   rE   r|   r   r   r   r   ro     s   	ro   r&   )r.   r   r"   r   c                 C  sB   | t kr4|d i gfkrtd|dkr.tdt S t|  ||S )Nz4Read preference primary cannot be combined with tagsr&   zCRead preference primary cannot be combined with maxStalenessSeconds)r[   r
   rY   _ALL_READ_PREFERENCES)r.   r   r"   r   r   r   make_read_preference  s    r~   )rt   rw   	SECONDARYSECONDARY_PREFERREDNEARESTc                   @  s.   e Zd ZdZe Ze Ze Z	e
 Ze ZdS )rs   a  An enum that defines some commonly used read preference modes.

    Apps can also create a custom read preference, for example::

       Nearest(tag_sets=[{"node":"analytics"}])

    See :doc:`/examples/high_availability` for code examples.

    A read preference is used in three cases:

    :class:`~pymongo.mongo_client.MongoClient` connected to a single mongod:

    - ``PRIMARY``: Queries are allowed if the server is standalone or a replica
      set primary.
    - All other modes allow queries to standalone servers, to a replica set
      primary, or to replica set secondaries.

    :class:`~pymongo.mongo_client.MongoClient` initialized with the
    ``replicaSet`` option:

    - ``PRIMARY``: Read from the primary. This is the default, and provides the
      strongest consistency. If no primary is available, raise
      :class:`~pymongo.errors.AutoReconnect`.

    - ``PRIMARY_PREFERRED``: Read from the primary if available, or if there is
      none, read from a secondary.

    - ``SECONDARY``: Read from a secondary. If no secondary is available,
      raise :class:`~pymongo.errors.AutoReconnect`.

    - ``SECONDARY_PREFERRED``: Read from a secondary if available, otherwise
      from the primary.

    - ``NEAREST``: Read from any member.

    :class:`~pymongo.mongo_client.MongoClient` connected to a mongos, with a
    sharded cluster of replica sets:

    - ``PRIMARY``: Read from the primary of the shard, or raise
      :class:`~pymongo.errors.OperationFailure` if there is none.
      This is the default.

    - ``PRIMARY_PREFERRED``: Read from the primary of the shard, or if there is
      none, read from a secondary of the shard.

    - ``SECONDARY``: Read from a secondary of the shard, or raise
      :class:`~pymongo.errors.OperationFailure` if there is none.

    - ``SECONDARY_PREFERRED``: Read from a secondary of the shard if available,
      otherwise from the shard primary.

    - ``NEAREST``: Read from any shard member.
    N)r:   rT   rU   rV   rY   rt   r`   rw   rh   r   rk   r   rm   r   r   r   r   r   rs     s   6rs   rz   c                 C  s
   t | S )z2Get the read preference mode from mongos/uri name.)r0   index)r<   r   r   r   read_pref_mode_from_nameS  s    r   c                   @  sT   e Zd ZU dZded< ddddZddd	d
dZddddZddddZdS )MovingAveragez0Tracks an exponentially-weighted moving average.zOptional[float]averager-   r8   c                 C  s
   d | _ d S r/   r   r;   r   r   r   r7   ]  s    zMovingAverage.__init__float)sampler   c                 C  s@   |dk rt d| | jd kr(|| _nd| j d|  | _d S )Nr   zduration cannot be negative g?g?)r   r   )r6   r   r   r   r   
add_sample`  s
    
zMovingAverage.add_samplec                 C  s   | j S )z6Get the calculated average, or None if no samples yet.r   r;   r   r   r   getj  s    zMovingAverage.getc                 C  s
   d | _ d S r/   r   r;   r   r   r   resetn  s    zMovingAverage.resetN)	r:   rT   rU   rV   __annotations__r7   r   r   r   r   r   r   r   r   X  s   

r   N)r&   )/rV   
__future__r   collectionsr   typingr   r   r   r   r   Zpymongor	   Zpymongo.errorsr
   Zpymongo.server_selectorsr   r   r   Zpymongo.topology_descriptionr   r[   rb   ri   rl   rn   r0   r!   Z_Hedger@   r    r$   r'   r+   r,   rY   r`   rh   rk   rm   ro   r}   r~   Z_MODESrs   r   r   r   r   r   r   <module>   sH    /'0'( 	>